我想知道为什么我的用户控件无法正常工作

问题描述 投票:0回答:1

我想使用RangeSlider,而不使用extendToolkit包。 所以我做了一个 RangeSlider 并开始测试。

NormalSlider 在没有 Bindingmode.Twoway 的情况下也能正常工作,所以我没有向任何绑定添加twoway。

测试结果

当我拖动普通滑块时,这个值会改变。

  1. normalSlider.Value
  2. TTTTT.价值
  3. rangeSlider.LowValue

当我拖动rangeSlider时,这个值会改变。

  1. rangeSlider.LowValue

我再次拖动normalSlider,这个值就改变了;

  1. normalSlider.Value
  2. TTTTT.价值

我想要的正常操作:

当我拖动普通滑块时,这个值应该改变。

  1. normalSlider.Value
  2. TTTTT.价值
  3. rangeSlider.LowValue

当我拖动 rangeSlider 时,这个值应该改变。

  1. normalSlider.Value
  2. TTTTT.价值
  3. rangeSlider.LowValue
<Window Name="window" x:Class="CommonTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:CommonTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <Grid Background="Gray" DataContext="{Binding ElementName=window}">
        <StackPanel>
            <Slider Name="normalSlider" Value="{Binding TTTTTT.LowValue}" Maximum="100" Minimum="0"/>
            <TextBlock Text="Binding Element"/>
            <TextBlock Text="{Binding Value,ElementName=normalSlider}"/>
            <TextBlock Text="Binding TTTTTT"/>
            <TextBlock Text="{Binding TTTTTT.LowValue}"/>
            <local:RangeSlider Name="rangeSlider" ValueChanged="rangeSlider_ValueChanged"  IsRangeVisiable="True"  LowValue="{Binding TTTTTT.LowValue}" HighValue="{Binding TTTTTT.HighValue}" Minimum="0" Maximum="100"/>
            <TextBlock Text="Binding Element"/>
            <TextBlock Text="{Binding LowValue,ElementName=rangeSlider}"/>
            <TextBlock Text="Binding TTTTTT"/>
            <TextBlock Text="{Binding TTTTTT.LowValue}"/>
        </StackPanel>
    </Grid>
</Window>

这里的背后代码

 public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public bool Set<T>(ref T field, T value, [CallerMemberName] string propertyName = "")
        {
            if (EqualityComparer<T>.Default.Equals(field, value))
                return false;

            field = value;
            RaiseOnPropertyChanged(propertyName);
            return true;
        }

        private void RaiseOnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));


        private MyTestValues tTTTTT;

        public MyTestValues TTTTTT { get => tTTTTT; set => Set(ref tTTTTT, value); }
        public MainWindow()
        {
            TTTTTT = new MyTestValues { HighValue = 100, LowValue = 0 };
            InitializeComponent();
        }

        private void rangeSlider_ValueChanged(SliderValueType arg1, double arg2)
        {
            Console.WriteLine("Range Slider Value is Changed");
        }
    }

    public class MyTestValues : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        public bool Set<T>(ref T field, T value, [CallerMemberName] string propertyName = "")
        {
            if (EqualityComparer<T>.Default.Equals(field, value))
                return false;

            field = value;
            RaiseOnPropertyChanged(propertyName);
            return true;
        }

        private void RaiseOnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));




        private double lowValue;
        private double highValue;


        public double LowValue { get => lowValue; set => Set(ref lowValue, value); }
        public double HighValue { get => highValue; set => Set(ref highValue, value); }
    }

这是我的 RangeSlider 代码

<UserControl Name="userControl" x:Class="Common.Control.RangeSlider"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:cvt="clr-namespace:Common.Control.Converters"
             xmlns:local="clr-namespace:Common.Control"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <UserControl.Resources>
        <ResourceDictionary>
            <cvt:BoolToVisibilityConverter x:Key="cvtBoolToVisibility"/>
            <Style TargetType="Thumb">
                <Setter Property="Background" Value="Red"/>
                <Setter Property="Width" Value="30"/>
                <Setter Property="Height" Value="20"/>
                <Setter Property="VerticalAlignment" Value="Center"/>
                <Setter Property="HorizontalAlignment" Value="Left"/>
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="Thumb">
                            <Border Background="{Binding Background, RelativeSource={RelativeSource TemplatedParent}}" CornerRadius="10"/>
                            <ControlTemplate.Triggers>
                                <Trigger Property="IsMouseOver" Value="True">
                                    <Setter Property="Background" Value="Green"/>
                                </Trigger>
                                <Trigger Property="IsDragging" Value="True">
                                    <Setter Property="Background" Value="Blue"/>
                                </Trigger>
                            </ControlTemplate.Triggers>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </ResourceDictionary>
    </UserControl.Resources>

    <Grid PreviewMouseDown="rangeSliderArea_PreviewMouseDown" Margin="10" DataContext="{Binding ElementName=userControl}">
        <Border Name="backGroundArea" Height="5"/>
        <Border Name="rangeArea" Height="5" Background="White" Visibility="{Binding IsRangeVisiable,Converter={StaticResource cvtBoolToVisibility}}"/>
        <Grid Name="rangeSliderArea">
            <Thumb Name="lowThumb" Thumb.DragStarted="DragStarted" Thumb.DragCompleted="DragCompleted" Thumb.DragDelta="DragDelta">
                <Thumb.RenderTransform>
                    <TranslateTransform X="{Binding LowPosX}"/>
                </Thumb.RenderTransform>
            </Thumb>

            <Thumb Name="highThumb" Thumb.DragStarted="DragStarted" Thumb.DragCompleted="DragCompleted" Thumb.DragDelta="DragDelta">
                <Thumb.RenderTransform>
                    <TranslateTransform X="{Binding HighPosX}"/>
                </Thumb.RenderTransform>
            </Thumb>
        </Grid>
    </Grid>
</UserControl>
namespace Common.Control
{
    public enum SliderValueType
    {
        Low,
        High
    }
    public partial class RangeSlider : UserControl, INotifyPropertyChanged
    {
        #region DP
        public static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(RangeSlider), new PropertyMetadata(false));
        public static readonly DependencyProperty IsRangeVisiableProperty = DependencyProperty.Register("IsRangeVisiable", typeof(bool), typeof(RangeSlider), new PropertyMetadata(true));

        public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register("Maximum", typeof(double), typeof(RangeSlider), new PropertyMetadata(100.0, MaximumPropertyChanged));
        public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register("Minimum", typeof(double), typeof(RangeSlider), new PropertyMetadata(0.0, MinimumPropertyChanged));
        public static readonly DependencyProperty LowValueProperty = DependencyProperty.Register("LowValue", typeof(double), typeof(RangeSlider), new PropertyMetadata(0.0, LowValuePropertyChanged));
        public static readonly DependencyProperty HighValueProperty = DependencyProperty.Register("HighValue", typeof(double), typeof(RangeSlider), new PropertyMetadata(0.0, HighValuePropertyChanged));
        public static readonly DependencyProperty OrientationProperty = DependencyProperty.Register("Orientation", typeof(Orientation), typeof(RangeSlider), new PropertyMetadata(Orientation.Horizontal, OrientationPropertyChanged));
        public bool IsReadOnly { get => (bool)GetValue(IsReadOnlyProperty); set => SetValue(IsReadOnlyProperty, value); }
        public bool IsRangeVisiable { get => (bool)GetValue(IsRangeVisiableProperty); set => SetValue(IsRangeVisiableProperty, value); }
        public double Maximum { get => (double)GetValue(MaximumProperty); set => SetValue(MaximumProperty, value); }
        public double Minimum { get => (double)GetValue(MinimumProperty); set => SetValue(MinimumProperty, value); }
        public double LowValue { get => (double)GetValue(LowValueProperty); set => SetValue(LowValueProperty, value); }
        public double HighValue { get => (double)GetValue(HighValueProperty); set => SetValue(HighValueProperty, value); }
        public Orientation Orientation { get => (Orientation)GetValue(OrientationProperty); set => SetValue(OrientationProperty, value); }
        private static void MaximumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = d as RangeSlider;
            sender.UpdateView(SliderValueType.Low);
            sender.UpdateView(SliderValueType.High);
        }
        private static void MinimumPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = d as RangeSlider;
            sender.UpdateView(SliderValueType.Low);
            sender.UpdateView(SliderValueType.High);
        }
        private static void LowValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = d as RangeSlider;
            sender.CheckInRange(SliderValueType.Low);
            sender.UpdateView(SliderValueType.Low);
            sender.ValueChanged?.Invoke(SliderValueType.Low, sender.LowValue);
        }
        private static void HighValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = d as RangeSlider;
            sender.CheckInRange(SliderValueType.High);
            sender.UpdateView(SliderValueType.High);
            sender.ValueChanged?.Invoke(SliderValueType.High, sender.HighValue);
        }
        private static void OrientationPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = d as RangeSlider;
        }
        #endregion

        #region Variables
        private double lowPosX;
        private double highPosX;
        #endregion

        #region Events
        public event Action<SliderValueType, double> ValueChanged;
        public event PropertyChangedEventHandler PropertyChanged;
        public bool Set<T>(ref T field, T value, [CallerMemberName] string propertyName = "")
        {
            if (EqualityComparer<T>.Default.Equals(field, value))
                return false;

            field = value;
            RaiseOnPropertyChanged(propertyName);
            return true;
        }



        private void RaiseOnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        #endregion

        #region Properties

        private double Range_value => Maximum - Minimum;
        private double SliderWidth_Pixel => rangeSliderArea.ActualWidth - lowThumb.Width;
        private double ValuePerPixel => SliderWidth_Pixel / Range_value;

        public double LowPosX { get => lowPosX; set => Set(ref lowPosX, value); }
        public double HighPosX { get => highPosX; set => Set(ref highPosX, value); }

        #endregion

        #region Constructor

        public RangeSlider()
        {
            InitializeComponent();
            Loaded += RangeSlider_Loaded;
            SizeChanged += RangeSlider_SizeChanged;
        }
        #endregion

        #region Finalizer
        ~RangeSlider()
        {

        }
        #endregion

        #region Functions
        private void UpdateView(SliderValueType valueType)
        {
            switch (valueType)
            {
                case SliderValueType.Low:
                    if (Orientation == Orientation.Horizontal)
                    {
                        var distanceFromLeft_pixel = (LowValue - Minimum) * ValuePerPixel;
                        var distanceHighLow = (HighValue - LowValue) * ValuePerPixel;
                        rangeArea.Margin = new Thickness(distanceFromLeft_pixel, 0, SliderWidth_Pixel - distanceFromLeft_pixel - distanceHighLow, 0);
                        LowPosX = distanceFromLeft_pixel;
                    }
                    else
                    {

                    }
                    break;
                case SliderValueType.High:
                    if (Orientation == Orientation.Horizontal)
                    {
                        var distanceFromLeft_pixel = (HighValue - Minimum) * ValuePerPixel;
                        var distanceHighLow = (HighValue - LowValue) * ValuePerPixel;
                        rangeArea.Margin = new Thickness(distanceFromLeft_pixel - distanceHighLow, 0, SliderWidth_Pixel - distanceFromLeft_pixel, 0);
                        HighPosX = distanceFromLeft_pixel;
                    }
                    else
                    {

                    }
                    break;
            }
        }
        private void CheckInRange(SliderValueType sliderValue)
        {
            switch (sliderValue)
            {
                case SliderValueType.Low:
                    if (LowValue > HighValue) LowValue = HighValue;
                    if (LowValue < Minimum) LowValue = Minimum;
                    break;
                case SliderValueType.High:
                    if (HighValue < LowValue) HighValue = LowValue;
                    if (HighValue > Maximum) HighValue = Maximum;
                    break;
            }
        }
        private void MoveBlockTo(Point point, SliderValueType block)
        {
            double position;
            if (Orientation == Orientation.Horizontal)
            {
                position = point.X;
            }
            else
            {
                position = point.Y;
            }

            var value = Math.Min(Maximum, Minimum + (position / SliderWidth_Pixel) * (Maximum - Minimum));
            if (block == SliderValueType.Low)
            {
                LowValue = value;
            }
            else if (block == SliderValueType.High)
            {
                HighValue = value;
            }
        }
        #region Event_Functions
        private void RangeSlider_Loaded(object sender, RoutedEventArgs e)
        {
            UpdateView(SliderValueType.Low);
            UpdateView(SliderValueType.High);
        }
        private void RangeSlider_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            UpdateView(SliderValueType.Low);
            UpdateView(SliderValueType.High);
        }


        private void rangeSliderArea_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (!IsReadOnly)
            {
                if ((lowThumb != null && lowThumb.IsMouseOver) || (highThumb != null && highThumb.IsMouseOver))
                    return;

                var point = e.GetPosition(rangeSliderArea);
                if (e.ChangedButton == MouseButton.Left)
                {
                    MoveBlockTo(point, SliderValueType.Low);
                }
                else if (e.ChangedButton == MouseButton.Right)
                {
                    MoveBlockTo(point, SliderValueType.High);
                }
                e.Handled = true;
            }
        }


        private void DragStarted(object sender, DragStartedEventArgs e)
        {

        }

        private void DragCompleted(object sender, DragCompletedEventArgs e)
        {

        }

        private void DragDelta(object sender, DragDeltaEventArgs e)
        {
            if (!IsReadOnly && e.OriginalSource is Thumb thumb && rangeSliderArea != null)
            {
                double changedValue = 0.0;
                if (Orientation == Orientation.Horizontal)
                {
                    var currentChange_PixelWidth = e.HorizontalChange;
                    changedValue = currentChange_PixelWidth / ValuePerPixel;
                }
                if (thumb == lowThumb)
                {
                    LowValue += changedValue;
                }
                else if (thumb == highThumb)
                {
                    HighValue += changedValue;
                }
            }
        }
        #endregion

        #endregion
    }
}
c# wpf data-binding
1个回答
0
投票

您是否知道为什么默认 Slider 的 Value 继承了 RangeBase.Value 依赖属性在 TwoWay 模式下工作而无需在 xaml 中显式设置?这是因为该依赖属性是通过指定

FrameworkPropertyMetadataOptions.BindsTwoWayByDefault
来定义的。请参阅来源

如果您想让 LowValue 依赖属性工作而无需在 xaml 中显式设置 TwoWay,则需要按照如下所示的相同方式定义它。

public static readonly DependencyProperty LowValueProperty = DependencyProperty.Register(
    "LowValue",
    typeof(double),
    typeof(RangeSlider),
    new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, LowValuePropertyChanged));

顺便说明一下,对于继承 DependencyObject 的视图元素的数据绑定,依赖属性比添加 INotifyPropertyChanged 更合适。

© www.soinside.com 2019 - 2024. All rights reserved.