【问题标题】:How to use command bindings in user controls in wpf?如何在 wpf 的用户控件中使用命令绑定?
【发布时间】:2012-10-23 16:56:48
【问题描述】:

在 MainWindow 中,命令绑定工作正常。 在 UserControl1 中它不起作用。请注意,datacontext 设置正确,绑定结果的按钮内容证明了这一点。

我没有尝试将用户控件中的命令绑定到主窗口中的命令或任何其他此类诡计。我只是想在 UserControl1 的 MainWindow 中复制我所做的。

主窗口 XAML

<StackPanel>
    <Button Content="Click Here" Command="{Binding ClickHereCommand}" Height="25" Width="90"></Button>
    <local:UserControl1></local:UserControl1>
</StackPanel>

MainWindow 代码背后

public partial class MainWindow : Window
{
    public static RoutedCommand ClickHereCommand { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
        ClickHereCommand = new RoutedCommand();
        CommandBindings.Add(new CommandBinding(ClickHereCommand, ClickHereExecuted));
    }

    public void ClickHereExecuted(object sender, ExecutedRoutedEventArgs e)
    {
        System.Windows.MessageBox.Show("hello");
    }
}

用户控件 XAML

<UserControl x:Class="CommandBindingTest.UserControl1"
         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" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300" x:Name="root">

<Grid DataContext="{Binding ElementName=root}" >
    <Button Content="{Binding ButtonContent}" Command="{Binding ClickHereCommand}" Height="25" Width="90"></Button>
</Grid>
</UserControl>

后面的用户控制代码

public partial class UserControl1 : UserControl, INotifyPropertyChanged
{
    private string _ButtonContent;
    public string ButtonContent
    {
        get { return _ButtonContent; }
        set
        {
            if (_ButtonContent != value)
            {
                _ButtonContent = value;
                OnPropertyChanged("ButtonContent");
            }
        }
    }

    public static RoutedCommand ClickHereCommand { get; set; }


    public UserControl1()
    {
        InitializeComponent();
        ClickHereCommand = new RoutedCommand();
        CommandBindings.Add(new CommandBinding(ClickHereCommand, ClickHereExecuted));
        ButtonContent = "Click Here";
    }

    public void ClickHereExecuted(object sender, ExecutedRoutedEventArgs e)
    {
        System.Windows.MessageBox.Show("hello from UserControl1");
    }


    #region INotifyPropertyChanged Members
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
    #endregion
}

【问题讨论】:

  • 您是否正在检查“输出”窗口是否有错误?有吗?

标签: c# .net wpf


【解决方案1】:

这是最好的解决方案:

 <Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" >
        <Button Content="{Binding ButtonContent}" Command="{Binding ClickHereCommand}" Height="25" Width="90"></Button>
 </Grid>

其他解决方案:

您忘记将 DataContext 设置为 UserControl1

  public UserControl1()
        {
            InitializeComponent();
            ClickHereCommand = new RoutedCommand();
            CommandBindings.Add(new CommandBinding(ClickHereCommand, ClickHereExecuted));
            ButtonContent = "Click Here";
            this.DataContext = this;
        }

在此之后,您必须删除 UserControl1 Grid 中的 DataContext。

这个:

<Grid DataContext="{Binding ElementName=root}" >
    <Button Content="{Binding ButtonContent}" Command="{Binding ClickHereCommand}" Height="25" Width="90"></Button>
</Grid>

你必须改成这样:

<Grid>
        <Button Content="{Binding ButtonContent}" Command="{Binding ClickHereCommand}" Height="25" Width="90"></Button>
</Grid>

UserControl中不设置DataContext的解决方案:

您必须将 ButtonContent 和 ClickHereCommand 更改为 DependencyProperty。

        public string ButtonContent
        {
            get { return (string)GetValue(ButtonContentProperty); }
            set { SetValue(ButtonContentProperty, value); }
        }

        public static readonly DependencyProperty ButtonContentProperty =
            DependencyProperty.Register("ButtonContent", typeof(string), typeof(UserControl1), new UIPropertyMetadata(string.Empty));

        public RoutedCommand ClickHereCommand
        {
            get { return (RoutedCommand)GetValue(ClickHereCommandProperty); }
            set { SetValue(ClickHereCommandProperty, value); }
        }

        public static readonly DependencyProperty ClickHereCommandProperty =
            DependencyProperty.Register("ClickHereCommand", typeof(RoutedCommand), typeof(UserControl1), new UIPropertyMetadata(null));

UserControl1的ctor中:

 public UserControl1()
    {
        InitializeComponent();

        ClickHereCommand = new RoutedCommand();
        CommandBindings.Add(new CommandBinding(ClickHereCommand, ClickHereExecuted));            
        ButtonContent = "Click Here";
        //this.DataContext = this;
    }

【讨论】:

  • 感谢您的回答,但我认为这不正确。正如我在最初的问题中所说,“数据上下文设置正确,正如按钮的内容所证明的那样”。我故意以这种方式设置 datacontext,因为在我的应用程序中 UserControl 将包含依赖属性,我特别不想设置 DataContext = this。
  • 根据您的回复,我在网格中更改了 DataContext,如下所示:DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" 并且它有效。很奇怪,按钮内容的绑定工作正常,但命令绑定却没有.....
  • @Sam 当我第一次使用您的代码时,Button 内容的绑定对我不起作用。当您将属性更改为 DependencyProperty 时,它将起作用。在这种情况下,您可以使用绑定DataContext="{Binding ElementName=root}"
  • 没有必要使这些属性依赖于属性,因为它们不是绑定的目标(即它们在 MainWindow 中工作得很好)。这看起来像一些古怪的 wpf 错误。我想接受你写的作为答案,因为你把我引向了正确的方向,但是你的答案都没有达到目标。请按照我上面的描述建议设置数据上下文,我会接受你的回答(你的方向是正确的)。
  • 嗯...有趣,我看不出DataContext="{Binding ElementName=root}"DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" 之间有什么区别。在这两种情况下,DataContext 都是 UserControl1。
【解决方案2】:

在我发布这篇文章将近两年后,我又来了。我忘记了 WPF 的这个小功能,果然又被它咬了。

上面标记的答案部分正确,但包含其他令人分心和/或不正确的内容。为了清楚起见,我将强调问题是什么以及解决方案是什么。我这样做是为了我的利益而不是为了你的利益,因为我确信我会在大约 16 个月后回到这里。

这就是问题所在。不要这样做。除了路由命令之外,您的绑定都将继续工作:

<UserControl x:Class="CommandBindingTest.UserControl1"
     // snip
     d:DesignHeight="300" d:DesignWidth="300" x:Name="root">

    <Grid DataContext="{Binding ElementName=root}" >
        <Button Content="{Binding ButtonContent}" Command="{Binding ClickHereCommand}" Height="25" Width="90">
        </Button>
    </Grid>

解决方案是使用相对源来设置数据上下文,如下所示:

<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" >
    <Button Content="{Binding ButtonContent}" Command="{Binding ClickHereCommand}" Height="25" Width="90">
    </Button>
</Grid>

我所问的问题的唯一答案是上面的那个(实际上可能还有其他答案,但到目前为止在这个问题中还没有讨论过)。 具体来说: 在用户控件中设置“DataContext = this”不是解决方案,实际上会破坏承载用户控件的控件中的绑定。 此外,只有作为绑定目标的属性必须是依赖属性。这个建议是不正确的。

【讨论】:

  • 原来你的问题只是对如何在 UserControls 中绑定 DataContext 的误解。如果你得到这个,你会永远记得。阅读stackoverflow.com/a/19199045/711061很有启发性
  • @sam 您提供的链接中的解决方案与此问题中提供的解决方案有何不同?
  • 虽然解决方法都是一样的,但你不知道为什么要写那个代码;因此,您偶尔会在这个问题上绊倒。我只是想进一步说明这段代码的含义。
猜你喜欢
  • 2011-08-18
  • 2019-10-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多