UserControl绑定到observablecollection不起作用

时间:2017-04-05 14:34:19

标签: c# wpf

我环顾四周但仍无法找到解决方案...

我创建了一个UserControl,它基本上是一个滑块,但几乎没有自定义功能。

public partial class CustomSlider : UserControl
{
    public CustomSlider()
    {
        InitializeComponent();
        this.DataContext = this;

        CMiXSlider.ApplyTemplate();
        Thumb thumb0 = (CMiXSlider.Template.FindName("PART_Track", CMiXSlider) as Track).Thumb;
        thumb0.MouseEnter += new MouseEventHandler(thumb_MouseEnter);
    }

    private void thumb_MouseEnter(object sender, MouseEventArgs e)
    {
        if (e.LeftButton == MouseButtonState.Pressed && e.MouseDevice.Captured == null)
        {
            MouseButtonEventArgs args = new MouseButtonEventArgs(e.MouseDevice, e.Timestamp, MouseButton.Left);
            args.RoutedEvent = MouseLeftButtonDownEvent;
            (sender as Thumb).RaiseEvent(args);
        }
    }

    public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register("Value", typeof(double), typeof(CustomSlider), new PropertyMetadata(0.0));
    public double Value
    {
        get { return (double)this.GetValue(ValueProperty); }
        set { this.SetValue(ValueProperty, value); }
    }
}

XAML:

<UserControl x:Class="CMiX.CustomSlider"
         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:local="clr-namespace:CMiX"
         mc:Ignorable="d" 
         d:DesignHeight="139.8" d:DesignWidth="546.2">
<UserControl.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/CMiX_UserControl;component/RessourceDictionnaries/Brushes/GenericBrushes.xaml"/>
            <ResourceDictionary Source="/CMiX_UserControl;component/RessourceDictionnaries/Styles/BaseSliderStyle.xaml"/>
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</UserControl.Resources>
<Grid>
    <Slider x:Name="CMiXSlider" Style="{StaticResource BaseSliderStyle}" 
            Value="{Binding Value, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type local:CustomSlider}}}" 
            IsMoveToPointEnabled="True" Minimum="0.0" Maximum="1.0"/>
</Grid>

然后我在另一个UserControl内使用它:

<CMiX:CustomSlider x:Name="SliderTest" Grid.Row="2" Value="{Binding ChannelsAlpha[0], Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

我正在尝试绑定到此ObservableCollection(相同的滑块将被使用6次):

private ObservableCollection<double> _ChannelsAlpha = new ObservableCollection<double>(new[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 });
    public ObservableCollection<double> ChannelsAlpha
    {
        get { return _ChannelsAlpha; }
        set { _ChannelsAlpha = value; }
    }

问题是,绑定不会以任何方式发生。而我特别不知道的是,如果我使用这个标准滑块:

<Slider x:Name="Ch0_Alpha" Margin="1" IsMoveToPointEnabled="True" Minimum="0.0" Maximum="1.0" Orientation="Horizontal" Value="{Binding DataContext.ChannelsAlpha[0], ElementName=Ch0_Alpha, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>

然后它按预期工作。

1 个答案:

答案 0 :(得分:3)

您无法绑定任何内容,因为您非常小心地破坏了DataContext:

this.DataContext = this;
不要这样做。要绑定到UserControl本身的属性,请使用RelativeSource绑定,例如......与您已使用的绑定完全相同:

<Slider 
    x:Name="CMiXSlider" 
    Style="{StaticResource BaseSliderStyle}" 
    Value="{Binding Value, Mode=TwoWay, RelativeSource={RelativeSource AncestorType={x:Type local:CustomSlider}}}" 
    IsMoveToPointEnabled="True" 
    Minimum="0.0" 
    Maximum="1.0"
    />

我没有看到你在UserControl XAML中使用DataContext的任何地方,所以我只是在构造函数中删除该行并去喝啤酒。

嗯,首先,为了良好的形式,我将摆脱构造函数中的模板内容,并将其移至OnApplyTemplate:

public CustomSlider()
{
    InitializeComponent();
}

public override void OnApplyTemplate()
{
    Thumb thumb0 = (CMiXSlider.Template.FindName("PART_Track", CMiXSlider) as Track).Thumb;
    thumb0.MouseEnter += new MouseEventHandler(thumb_MouseEnter);
}
然后是啤酒。

P.S。以下是如何调试绑定:

Value="{Binding ChannelsAlpha[0], PresentationTraceSources.TraceMode=High, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"

添加PresentationTraceSources.TraceMode=High,然后在运行时观看VS中的“输出”窗口。它将告诉您尝试解决绑定所需的所有步骤。它在这种情况下告诉你的是,它在ChannelIsAlpha的实例上寻找CMiX:CustomSlider而不是在viewmodel上。这就是你的线索,即通过摆脱DataContext的继承值来创建问题。在构造函数中,this.DataContext是父视图的viewmodel,直到您将其设置为this(在断点中弹出并查看)。

这就是为什么我们这些胡思乱想的老人有这样一个关于不设置this.DataContext = this;的毛刺的原因。首先,如果它是相对无害的,那么你开始认为它只是必要的样板,而你就是这样。事实上,它永远不会必要。

Bindings和DataContext很难习惯,因为这种隐含的事情正在发生。起初我觉得很奇怪。请记住,默认情况下,所有绑定都希望绑定到viewmodel,并且您应始终能够假设无论身在何处,DataContext都将成为viewmodel。永远不要明确地设置DataContext

绑定到除viewmodel之外的任何内容都是一种特殊情况:RelativeSource={RelativeSource AncestorType=FooBar}Source={StaticResource WhateverKey}ElementName=FooBarListBoxRelativeSource={RelativeSource Self}或其他。