WPF自定义Button用户控件绑定RelayCommand无响应求助
我开发了一款包含图标、自定义文本块的Button用户控件,通过DependencyProperty传递Command,但点击时Command无响应。相同的RelayCommand代码在普通Button上可正常执行。基于.NET Framework 4.8.1的测试项目代码如下:
项目代码
MainWindow.xaml
<Window x:Class="wpfuitest1.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:wpfuitest1" xmlns:userControl="clr-namespace:wpfuitest1" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <StackPanel> <userControl:boton Command="{Binding TestCommand}" ButtonContent="Non working Command button"/> <Button Content="Working Command button" Command="{Binding TestCommand}"></Button> </StackPanel> </Grid> </Window>
MainWindow.xaml.cs
using System.Windows; namespace wpfuitest1 { public partial class MainWindow : Window { private readonly VM vm = new VM(); public MainWindow() { InitializeComponent(); this.DataContext = vm; } } }
VM.cs
using System; namespace wpfuitest1 { internal class VM { public RelayCommand TestCommand => new RelayCommand(execute => Test(), canExecute => { return true; }); public void Test() { Console.WriteLine("test"); } } }
RelayCommand.cs
using System; using System.Windows.Input; namespace wpfuitest1 { internal class RelayCommand : ICommand { private Action<object> execute; private Func<object, bool> canExecute; public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null) { this.execute = execute; this.canExecute = canExecute; } public bool CanExecute(object parameter = null) { return canExecute == null | canExecute(parameter); } public void Execute(object parameter) { execute(parameter); } } }
boton.xaml(自定义用户控件)
<UserControl x:Class="wpfuitest1.boton" 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:wpfuitest1" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Button Style="{DynamicResource ButtonStyle1}" Content="{Binding ButtonContent}" Command="{Binding Command}"> </Button> </UserControl>
boton.xaml.cs
using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace wpfuitest1 { public partial class boton : UserControl { public boton() { InitializeComponent(); this.DataContext = this; } public string ButtonContent { get { return (string)GetValue(ButtonContentProperty); } set { SetValue(ButtonContentProperty, value); } } public static readonly DependencyProperty ButtonContentProperty = DependencyProperty.Register("ButtonContent", typeof(string), typeof(boton), new PropertyMetadata("")); public ICommand Command { get { return (ICommand)GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } } public static readonly DependencyProperty CommandProperty = DependencyProperty.Register( "Command", typeof(ICommand), typeof(boton), new PropertyMetadata(null)); } }
问题原因
核心问题出在自定义控件boton的构造函数中设置了this.DataContext = this;,导致两个关键错误:
- 外部使用
userControl:boton Command="{Binding TestCommand}"时,绑定的数据源变成了boton实例本身,而非父级的VM,因此TestCommand无法正确赋值给控件的Command依赖属性。 - 内部Button的
Command="{Binding Command}"虽然能找到控件的Command属性,但由于外部绑定失效,该属性值始终为null,点击自然无响应。
解决方案
步骤1:移除自定义控件的DataContext赋值
删除boton.xaml.cs构造函数中的this.DataContext = this;代码,让控件继承父级的DataContext(即MainWindow的VM),确保外部的Command绑定能正确关联到VM的TestCommand。
步骤2:修改内部控件的绑定方式
使用ElementName或RelativeSource让内部Button直接绑定到自定义控件的依赖属性,避免DataContext冲突。
修改后的boton.xaml(使用ElementName)
<UserControl x:Class="wpfuitest1.boton" 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:wpfuitest1" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800" x:Name="RootControl"> <Button Style="{DynamicResource ButtonStyle1}" Content="{Binding ButtonContent, ElementName=RootControl}" Command="{Binding Command, ElementName=RootControl}"> </Button> </UserControl>
修改后的boton.xaml.cs
using System.Windows; using System.Windows.Controls; using System.Windows.Input; namespace wpfuitest1 { public partial class boton : UserControl { public boton() { InitializeComponent(); } public string ButtonContent { get { return (string)GetValue(ButtonContentProperty); } set { SetValue(ButtonContentProperty, value); } } public static readonly DependencyProperty ButtonContentProperty = DependencyProperty.Register("ButtonContent", typeof(string), typeof(boton), new PropertyMetadata("")); public ICommand Command { get { return (ICommand)GetValue(CommandProperty); } set { SetValue(CommandProperty, value); } } public static readonly DependencyProperty CommandProperty = DependencyProperty.Register( "Command", typeof(ICommand), typeof(boton), new PropertyMetadata(null)); } }
替代绑定方式(使用RelativeSource)
如果不想用ElementName,也可以用RelativeSource指定父级UserControl:
Content="{Binding ButtonContent, RelativeSource={RelativeSource AncestorType=UserControl}}" Command="{Binding Command, RelativeSource={RelativeSource AncestorType=UserControl}}"
内容的提问来源于stack exchange,提问作者Flacuchentxox
相关产品推荐
相关产品推荐

