using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using CPF.Input;
using System.ComponentModel;
namespace CPF.Controls
{
///
/// 表示可包含其他可视元素的可滚动区域。
///
[Description("表示可包含其他可视元素的可滚动区域")]
public class ScrollViewer : ContentControl
{
/////
///// 获取或设置一个值,该值指示是否允许滚动支持 IScrollInfo 接口的元素。
/////
//[PropertyMetadata(true)]
//public bool CanContentScroll
//{
// get { return GetValue(); }
// set { SetValue(value); }
//}
///
/// 获取或设置一个值,该值指示是否应显示水平 ScrollBar。
///
[PropertyMetadata(ScrollBarVisibility.Auto)]
public ScrollBarVisibility HorizontalScrollBarVisibility
{
get { return GetValue(); }
set { SetValue(value); }
}
///
/// 获取或设置一个值,该值指示是否应显示垂直 ScrollBar。
///
[PropertyMetadata(ScrollBarVisibility.Auto)]
public ScrollBarVisibility VerticalScrollBarVisibility
{
get { return GetValue(); }
set { SetValue(value); }
}
///
/// 水平 ScrollBar 是否可见。
///
public Visibility ComputedHorizontalScrollBarVisibility
{
get { return GetValue(); }
private set { SetValue(value); }
}
///
/// 垂直 ScrollBar 是否可见。
///
public Visibility ComputedVerticalScrollBarVisibility
{
get { return GetValue(); }
private set { SetValue(value); }
}
/////
///// 获取可见内容的垂直偏移量。
/////
//public double ContentVerticalOffset
//{
// get { return GetValue(); }
// set { SetValue(value); }
//}
/////
///// 获取可见内容的水平偏移量。
/////
//public double ContentHorizontalOffset
//{
// get { return GetValue(); }
// set { SetValue(value); }
//}
///
/// 获取包含盘区垂直大小的值
///
[NotCpfProperty]
public float ExtentHeight
{
get
{
if (scrollInfo == null)
{
return 0;
}
return scrollInfo.ExtentHeight;
}
}
///
/// 获取包含盘区水平大小的值
///
[NotCpfProperty]
public float ExtentWidth
{
get
{
if (scrollInfo == null)
{
return 0;
}
return scrollInfo.ExtentWidth;
}
}
///
/// ViewportWidth contains the horizontal size of the scrolling viewport.
///
///
/// ExtentWidth is only an output property; it can effectively be set by specifying
/// Width on this element.
///
[NotCpfProperty]
public float ViewportWidth
{
get
{
if (scrollInfo == null)
{
return 0;
}
return scrollInfo.ViewportWidth;
}
}
///
/// ViewportHeight contains the vertical size of the scrolling viewport.
///
///
/// ViewportHeight is only an output property; it can effectively be set by specifying
/// Height on this element.
///
[NotCpfProperty]
public float ViewportHeight
{
get
{
if (scrollInfo == null)
{
return 0;
}
return scrollInfo.ViewportHeight;
}
}
IScrollInfo scrollInfo;
///
/// Scroll content by one line to the top.
///
public void LineUp()
{
if (scrollInfo != null)
{
scrollInfo.LineUp();
}
}
///
/// Scroll content by one line to the bottom.
///
public void LineDown()
{
if (scrollInfo != null)
{
scrollInfo.LineDown();
}
}
///
/// Scroll content by one line to the left.
///
public void LineLeft()
{
if (scrollInfo != null)
{
scrollInfo.LineLeft();
}
}
///
/// Scroll content by one line to the right.
///
public void LineRight()
{
if (scrollInfo != null)
{
scrollInfo.LineRight();
}
}
///
/// Scroll content by one page to the top.
///
public void PageUp()
{
if (scrollInfo != null)
{
scrollInfo.PageUp();
}
}
///
/// Scroll content by one page to the bottom.
///
public void PageDown()
{
if (scrollInfo != null)
{
scrollInfo.PageDown();
}
}
///
/// Scroll content by one page to the left.
///
public void PageLeft()
{
if (scrollInfo != null)
{
scrollInfo.PageLeft();
}
}
///
/// Scroll content by one page to the right.
///
public void PageRight()
{
if (scrollInfo != null)
{
scrollInfo.PageRight();
}
}
/////
///// 0 and ExtentWidth less ViewportWidth"
/////
//public void SetHorizontalOffset(float offset)
//{
// if (scrollInfo != null)
// {
// scrollInfo.SetHorizontalOffset(offset);
// }
//}
/////
///// Set the VerticalOffset to the passed value.
///// An implementation may coerce this value into a valid range, typically inclusively between 0 and less .
/////
//public void SetVerticalOffset(float offset)
//{
// if (scrollInfo != null)
// {
// scrollInfo.SetVerticalOffset(offset);
// }
//}
protected override void InitializeComponent()
{
//var col = new ColumnDefinition { };
//var row = new RowDefinition { };
Children.Add(new Grid
{
Width = "100%",
Height = "100%",
ColumnDefinitions = { new ColumnDefinition { }, new ColumnDefinition { Width = "auto" } },
RowDefinitions = { new RowDefinition { }, new RowDefinition { Height = "auto" } },
Children = {
{new Border{Name="contentPresenter",Width="100%",Height="100%", BorderStroke="0", PresenterFor=this, } },
{new ScrollBar{ Width=20, Height="100%",Cursor=Cursors.Arrow, IncreaseLargeChanged=PageDown, DecreaseLargeChanged=PageUp, IncreaseSmallChange=LineDown, DecreaseSmallChange=LineUp,Bindings={
{ nameof(ScrollBar.Maximum),nameof(VerticalMaximum),this,BindingMode.TwoWay },
{ nameof(ScrollBar.Value),nameof(VerticalOffset),this,BindingMode.TwoWay},
{ nameof(ScrollBar.ViewportSize),nameof(VerticalViewportSize),2 },
{ nameof(Visibility),nameof(ComputedVerticalScrollBarVisibility),2},
{ nameof(IsEnabled),nameof(VerticalScrollBarVisibility),this,BindingMode.OneWay,a=>((ScrollBarVisibility)a)!=ScrollBarVisibility.Disabled },
//{ nameof(Visibility),nameof(Width),col,BindingMode.OneWayToSource,null,a=>((Visibility)a)==Visibility.Visible?(GridLength)((FloatField)Binding.Current.Owner.GetValue(nameof(ScrollBar.Width))).Value:(GridLength)0}
} },1,0 },
{new ScrollBar{ Orientation=Orientation.Horizontal,Width="100%", Height=20,Cursor=Cursors.Arrow, IncreaseLargeChanged=PageRight, DecreaseLargeChanged=PageLeft, IncreaseSmallChange=LineRight, DecreaseSmallChange=LineLeft,Bindings={
{ nameof(ScrollBar.Maximum),nameof(HorizontalMaximum),this,BindingMode.TwoWay },
{ nameof(ScrollBar.Value),nameof(HorizontalOffset),this,BindingMode.TwoWay},
{ nameof(ScrollBar.ViewportSize),nameof(HorizontalViewportSize),2 },
{ nameof(Visibility),nameof(ComputedHorizontalScrollBarVisibility),2},
{ nameof(IsEnabled),nameof(HorizontalScrollBarVisibility),this,BindingMode.OneWay,a=>((ScrollBarVisibility)a)!=ScrollBarVisibility.Disabled },
//{ nameof(Visibility),nameof(Height),row,BindingMode.OneWayToSource,null,a=>((Visibility)a)==Visibility.Visible?(GridLength)((FloatField)Binding.Current.Owner.GetValue(nameof(ScrollBar.Height))).Value:(GridLength)0 }
} },0,1 },
}
});
}
protected float VerticalViewportSize
{
get { return GetValue(); }
set { SetValue(value); }
}
protected float HorizontalViewportSize
{
get { return GetValue(); }
set { SetValue(value); }
}
///
/// 垂直偏移,0到ExtentHeight-ViewportHeight
///
public float VerticalOffset
{
get { return GetValue(); }
set { SetValue(value); }
}
///
/// 垂直最大值
///
[Browsable(false)]
public float VerticalMaximum
{
get { return GetValue(); }
private set { SetValue(value); }
}
///
/// 水平最大值
///
[Browsable(false)]
public float HorizontalMaximum
{
get { return GetValue(); }
private set { SetValue(value); }
}
///
/// 水平偏移,0到ExtentWidth-ViewportWidth
///
public float HorizontalOffset
{
get { return GetValue(); }
set { SetValue(value); }
}
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
base.OnMouseWheel(e);
if (!e.Handled)
{
if (scrollInfo != null)
{
if (e.IsTouch)
{
if (scrollInfo.CanHorizontallyScroll)
{
scrollInfo.SetHorizontalOffset(HorizontalOffset - e.Delta.X);
e.Handled = true;
}
if (scrollInfo.CanVerticallyScroll)
{
scrollInfo.SetVerticalOffset(VerticalOffset - e.Delta.Y);
e.Handled = true;
}
}
else
{
if (scrollInfo.CanVerticallyScroll)
{
if (e.Delta.Y < 0)
{
scrollInfo.MouseWheelUp();
}
else if (e.Delta.Y > 0)
{
scrollInfo.MouseWheelDown();
}
e.Handled = true;
}
if (scrollInfo.CanHorizontallyScroll)
{
if (e.Delta.X < 0)
{
scrollInfo.MouseWheelLeft();
}
else if (e.Delta.X > 0)
{
scrollInfo.MouseWheelRight();
}
e.Handled = true;
}
}
}
}
}
protected override void OnSetContentElement(UIElement contentPresenter, UIElement oldValue, UIElement newValue)
{
if (oldValue != null)
{
if (scrollInfo != null)
{
scrollInfo.ScrollChanged -= Scroll_ScrollChanged;
scrollInfo.ScrollOwner = null;
}
contentPresenter.Children.Remove(oldValue);
}
scrollInfo = null;
if (newValue != null)
{
scrollInfo = newValue as IScrollInfo;
if (scrollInfo == null)
{
scrollInfo = new ScrollContentPresenter { Child = newValue, Width = "100%", Height = "100%" };
}
scrollInfo.ScrollChanged += Scroll_ScrollChanged;
scrollInfo.ScrollOwner = this;
var n = scrollInfo as UIElement;
n.ClipToBounds = true;
contentPresenter.Children.Add(n);
Scroll_ScrollChanged(null, null);
}
}
protected override void OnLayoutUpdated()
{
base.OnLayoutUpdated();
Scroll_ScrollChanged(null, null);
}
private void Scroll_ScrollChanged(object sender, EventArgs e)
{
if (scrollInfo != null)
{
HorizontalMaximum = Math.Max(0, scrollInfo.ExtentWidth - scrollInfo.ViewportWidth);
VerticalMaximum = Math.Max(0, scrollInfo.ExtentHeight - scrollInfo.ViewportHeight);
HorizontalOffset = scrollInfo.HorizontalOffset;
VerticalOffset = scrollInfo.VerticalOffset;
VerticalViewportSize = scrollInfo.ViewportHeight;
HorizontalViewportSize = scrollInfo.ViewportWidth;
//var len = (float)(scrollInfo.ViewportHeight / scrollInfo.ExtentHeight);
//if (scrollInfo.ExtentHeight == 0)
//{
// len = 1;
//}
//len = Math.Min(1, len);
//VerticalThumbLength = new FloatField(len, Unit.Percent);
//len = (float)(scrollInfo.ViewportWidth / scrollInfo.ExtentWidth);
//if (scrollInfo.ExtentWidth == 0)
//{
// len = 1;
//}
//len = Math.Min(1, len);
//HorizontalThumbLength = new FloatField(len, Unit.Percent);
switch (HorizontalScrollBarVisibility)
{
case ScrollBarVisibility.Auto:
ComputedHorizontalScrollBarVisibility = scrollInfo.CanHorizontallyScroll ? Visibility.Visible : Visibility.Collapsed;
break;
case ScrollBarVisibility.Hidden:
ComputedHorizontalScrollBarVisibility = Visibility.Collapsed;
break;
case ScrollBarVisibility.Disabled:
case ScrollBarVisibility.Visible:
ComputedHorizontalScrollBarVisibility = Visibility.Visible;
break;
}
switch (VerticalScrollBarVisibility)
{
case ScrollBarVisibility.Auto:
ComputedVerticalScrollBarVisibility = scrollInfo.CanVerticallyScroll ? Visibility.Visible : Visibility.Collapsed;
break;
case ScrollBarVisibility.Hidden:
ComputedVerticalScrollBarVisibility = Visibility.Collapsed;
break;
case ScrollBarVisibility.Disabled:
case ScrollBarVisibility.Visible:
ComputedVerticalScrollBarVisibility = Visibility.Visible;
break;
}
OnScroll();
}
else
{
switch (HorizontalScrollBarVisibility)
{
case ScrollBarVisibility.Auto:
ComputedHorizontalScrollBarVisibility = Visibility.Collapsed;
break;
case ScrollBarVisibility.Hidden:
ComputedHorizontalScrollBarVisibility = Visibility.Collapsed;
break;
case ScrollBarVisibility.Disabled:
case ScrollBarVisibility.Visible:
ComputedHorizontalScrollBarVisibility = Visibility.Visible;
break;
}
switch (VerticalScrollBarVisibility)
{
case ScrollBarVisibility.Auto:
ComputedVerticalScrollBarVisibility = Visibility.Collapsed;
break;
case ScrollBarVisibility.Hidden:
ComputedVerticalScrollBarVisibility = Visibility.Collapsed;
break;
case ScrollBarVisibility.Disabled:
case ScrollBarVisibility.Visible:
ComputedVerticalScrollBarVisibility = Visibility.Visible;
break;
}
}
}
protected virtual void OnScroll()
{
}
[PropertyChanged(nameof(VerticalOffset))]
void OnVerticalOffset(object newValue, object oldValue, PropertyMetadataAttribute attribute)
{
if (scrollInfo != null)
{
scrollInfo.SetVerticalOffset((float)newValue);
}
}
[PropertyChanged(nameof(HorizontalOffset))]
void OnHorizontalOffset(object newValue, object oldValue, PropertyMetadataAttribute attribute)
{
if (scrollInfo != null)
{
scrollInfo.SetHorizontalOffset((float)newValue);
}
}
//protected override void OnPropertyChanged(string propertyName, object oldValue, object newValue, PropertyMetadataAttribute propertyMetadata)
//{
// if (propertyName == nameof(VerticalOffset))
// {
// if (scrollInfo != null)
// {
// scrollInfo.SetVerticalOffset((float)newValue);
// }
// }
// else if (propertyName == nameof(HorizontalOffset))
// {
// if (scrollInfo != null)
// {
// scrollInfo.SetHorizontalOffset((float)newValue);
// }
// }
// base.OnPropertyChanged(propertyName, oldValue, newValue, propertyMetadata);
//}
}
public enum ScrollBarVisibility
{
///
/// No scrollbars and no scrolling in this dimension.
///
Disabled = 0,
///
/// The scrollbar should be visible only if there is more content than fits in the viewport.
///
Auto,
///
/// The scrollbar should never be visible. No space should ever be reserved for the scrollbar.
///
Hidden,
///
/// The scrollbar should always be visible. Space should always be reserved for the scrollbar.
///
Visible,
}
}