none
自定义控件依赖属性绑定不起作用 RRS feed

  • 问题

  • usecontrol里面自定义了一个依赖属性

            public string ShowResult
            {
                get { return (string)GetValue(ShowResultProperty); }
                set { SetValue(ShowResultProperty, value); }
    
            }
    
            // Using a DependencyProperty as the backing store for MyProperty.  This enables animation, styling, binding, etc...
            public static DependencyProperty ShowResultProperty =
                DependencyProperty.Register("ShowResult", typeof(string), typeof(ShowControl), new PropertyMetadata("0"));

    这个usecontrol里面有个TextBlock,Text属性绑定了ShowResult

    在XAML界面有绑定自身

    DataContext="{Binding RelativeSource={RelativeSource Self}}

    然后再MainWindows窗口引用这个控件,并且ShowResult绑定ViewModel类里的一个属性(这个类实现INotifyPropertyChanged接口)

    同时Mainwindows里另外有个usecontrol2 也绑定了ViewModel类里的同一个属性。

    调试发现ViewModel类里这个属性被改变了,但是usecontrol控件里的TextBlock的Text属性没有变化。

    这是什么原因?是否是我设置有误?

    2016年7月5日 13:38

答案

  • 你好 Aragaki:

    这样绑定是可以的,例如:

    MyViewModel.cs

        class MyViewModel:BindableBase
        {
            private string _myProperty;
            public string MyProperty
            {
                get { return _myProperty; }
                set { SetProperty(ref _myProperty , value); }
            }
        }

    MyUserControl.xaml

    <UserControl x:Class="WPF_Test.MyUserControl"
                 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:WPF_Test"
                 mc:Ignorable="d" 
                 Name="root"
                 d:DesignHeight="300" d:DesignWidth="300">
        <StackPanel Orientation="Vertical" Background="Pink">
            <TextBlock Background="AliceBlue" Margin="10" Text="{Binding MyDependencyProperty, ElementName=root}"/>
        </StackPanel>
    </UserControl>
    

    MyUserControl.xaml.cs

        public partial class MyUserControl : UserControl
        {
            public MyUserControl()
            {
                InitializeComponent();
            }
            public string MyDependencyProperty
            {
                get { return (string)GetValue(MyDependencyPropertyProperty); }
                set { SetValue(MyDependencyPropertyProperty, value); }
            }
            public static readonly DependencyProperty MyDependencyPropertyProperty =
                DependencyProperty.Register("MyDependencyProperty", typeof(string), typeof(MyUserControl), new PropertyMetadata(default(string)));
        }

    MainWindow.xaml

    <Window x:Class="WPF_Test.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:WPF_Test"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <StackPanel Orientation="Vertical">
            <local:MyUserControl x:Name="MyUserControl" MyDependencyProperty="{Binding MyProperty}"/>
            <TextBlock Text="{Binding MyDependencyProperty,ElementName=MyUserControl}"/>
        </StackPanel>
    </Window>
    

    MainWindow.xaml.cs

        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                DataContext = new MyViewModel { MyProperty = "TEST,TEST,TEST" };
            }
        }

    这样写,无论是MyUserControl里面的TextBlock,还是和MyUserControl平级的TextBlock,都成功地绑定到了MyUserControl.MyDependencyProperty,进而间接地获得了MyViewModel.MyProperty的值:“TEST,TEST,TEST”。


    不知道你是怎么绑定的?

    • 已标记为答案 Aragaki 2016年7月7日 6:55
    2016年7月6日 17:12
  • 你好 Aragaki:

    1、绑定的源可以用Source指定(数据绑定),也可以用ElementName指定(元素绑定),在两者都没有指定的时候,绑定源就是DataContext。

    你的需求是TextBlock绑定到UserControl的属性,明显是元素绑定的范畴,因此指定ElementName比较合理。

    2、绑定时,根据数据更新方向来决定Mode的设置。请参考:

    https://msdn.microsoft.com/zh-cn/library/ms752347(v=vs.110).aspx

    3、在绑定中,“Path=”可以缺省。


    • 已编辑 Lymim 2016年7月7日 7:45
    • 已标记为答案 Aragaki 2016年7月7日 9:16
    2016年7月7日 7:42

全部回复

  • 自己解决了,发出来分享一下

    只要在各自的usercontrol里需要绑定的属性直接绑定到ViewModel。之后在Mainwindows的DataContext绑定ViewModel实例

    这样usercontrol因为被引用在Mainwindows,源路径就是ViewMode(

    DataContext="{Binding RelativeSource={RelativeSource Self}}

    需要去掉)

    2016年7月6日 0:45
  • 你好 Aragaki:

    你这样指定{RelativeSource Self},被引用的将是UserControl自己,而不是ViewModel。其实如果是ViewModel的话,你原来的代码就可以成功了。

    不设置Binding的Source属性的话,会默认将此属性设置为DataContext,而你使用了RelativeSource,就会覆盖这个默认设置。

    一般的,元素的DataContext属性有多种被设置的方式:

    • 在代码或XAML中设置元素的DataContext;
    • 从父级元素继承DataContext;
    • DataTemplate中的元素的DataContext默认为对应的Data;
    • Prism等框架为View创建的ViewModel会自动设置为View的DataContext;

    因此你的UserControl的DataContext是继承自MainWindow的,虽然这样满足你现在的需求,但可能你后面还会有对不同UserControl设置不同数据源的情况,这时你就需要为每个UserControl设置各自的DataContext。

    • 已编辑 Lymim 2016年7月6日 1:35
    2016年7月6日 1:27
  • 想说去掉绑定自身,然后Text绑定usecontrol里的自定义依赖属性,然后在Mainwindwo里将自定义依赖属性绑定ViewModel

    即 Text → 自定义依赖属性 → ViewModel 这样子绑定为什么无效?

    2016年7月6日 15:43
  • 你好 Aragaki:

    你这样指定{RelativeSource Self},被引用的将是UserControl自己,而不是ViewModel。其实如果是ViewModel的话,你原来的代码就可以成功了。

    不设置Binding的Source属性的话,会默认将此属性设置为DataContext,而你使用了RelativeSource,就会覆盖这个默认设置。

    一般的,元素的DataContext属性有多种被设置的方式:

    • 在代码或XAML中设置元素的DataContext;
    • 从父级元素继承DataContext;
    • DataTemplate中的元素的DataContext默认为对应的Data;
    • Prism等框架为View创建的ViewModel会自动设置为View的DataContext;

    因此你的UserControl的DataContext是继承自MainWindow的,虽然这样满足你现在的需求,但可能你后面还会有对不同UserControl设置不同数据源的情况,这时你就需要为每个UserControl设置各自的DataContext。

    想说去掉绑定自身,然后Text绑定usecontrol里的自定义依赖属性,然后在Mainwindwo里将自定义依赖属性绑定ViewModel

    即 Text → 自定义依赖属性 → ViewModel 这样子绑定为什么无效?


    2016年7月6日 15:44
  • 你好 Aragaki:

    这样绑定是可以的,例如:

    MyViewModel.cs

        class MyViewModel:BindableBase
        {
            private string _myProperty;
            public string MyProperty
            {
                get { return _myProperty; }
                set { SetProperty(ref _myProperty , value); }
            }
        }

    MyUserControl.xaml

    <UserControl x:Class="WPF_Test.MyUserControl"
                 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:WPF_Test"
                 mc:Ignorable="d" 
                 Name="root"
                 d:DesignHeight="300" d:DesignWidth="300">
        <StackPanel Orientation="Vertical" Background="Pink">
            <TextBlock Background="AliceBlue" Margin="10" Text="{Binding MyDependencyProperty, ElementName=root}"/>
        </StackPanel>
    </UserControl>
    

    MyUserControl.xaml.cs

        public partial class MyUserControl : UserControl
        {
            public MyUserControl()
            {
                InitializeComponent();
            }
            public string MyDependencyProperty
            {
                get { return (string)GetValue(MyDependencyPropertyProperty); }
                set { SetValue(MyDependencyPropertyProperty, value); }
            }
            public static readonly DependencyProperty MyDependencyPropertyProperty =
                DependencyProperty.Register("MyDependencyProperty", typeof(string), typeof(MyUserControl), new PropertyMetadata(default(string)));
        }

    MainWindow.xaml

    <Window x:Class="WPF_Test.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:WPF_Test"
            mc:Ignorable="d"
            Title="MainWindow" Height="350" Width="525">
        <StackPanel Orientation="Vertical">
            <local:MyUserControl x:Name="MyUserControl" MyDependencyProperty="{Binding MyProperty}"/>
            <TextBlock Text="{Binding MyDependencyProperty,ElementName=MyUserControl}"/>
        </StackPanel>
    </Window>
    

    MainWindow.xaml.cs

        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
                DataContext = new MyViewModel { MyProperty = "TEST,TEST,TEST" };
            }
        }

    这样写,无论是MyUserControl里面的TextBlock,还是和MyUserControl平级的TextBlock,都成功地绑定到了MyUserControl.MyDependencyProperty,进而间接地获得了MyViewModel.MyProperty的值:“TEST,TEST,TEST”。


    不知道你是怎么绑定的?

    • 已标记为答案 Aragaki 2016年7月7日 6:55
    2016年7月6日 17:12
  • 我也是这样绑定的,不过貌似{binding  Source Property} 没有加 源路径ElementName。实测添加路径之后就可以了

    不过如果要操作同一个源属性,貌似要把mode改为TwoWay

    另外请问下

    {binding Path =  Source Property,ElementName = XXXX} 是不是和{binding  Source Property,ElementName = XXXX} 效果是一样的(简单测试结果是相同的)

    2016年7月7日 6:55
  • 你好 Aragaki:

    1、绑定的源可以用Source指定(数据绑定),也可以用ElementName指定(元素绑定),在两者都没有指定的时候,绑定源就是DataContext。

    你的需求是TextBlock绑定到UserControl的属性,明显是元素绑定的范畴,因此指定ElementName比较合理。

    2、绑定时,根据数据更新方向来决定Mode的设置。请参考:

    https://msdn.microsoft.com/zh-cn/library/ms752347(v=vs.110).aspx

    3、在绑定中,“Path=”可以缺省。


    • 已编辑 Lymim 2016年7月7日 7:45
    • 已标记为答案 Aragaki 2016年7月7日 9:16
    2016年7月7日 7:42
  • 明白了,谢谢。
    2016年7月7日 9:16