UWP中UserControl绑定ICommand:x:Bind正常,ElementName绑定报错
问题描述
使用x:Bind绑定自定义UserControl的SendInputCommand属性时一切正常,但将MainPage中的绑定代码替换为{Binding DataContext.SendInputCommand, ElementName=MyPage}后,在mainpage.xaml.cs的InitializeComponent方法中会抛出未处理异常。但同样的绑定方式用在Button的Command属性上却能正常运行,请问问题出在哪里?
复现代码
MyControl1.xaml.cs
public sealed partial class MyUserControl1 : UserControl { public ICommand SendInputCommand { get { return (ICommand)GetValue(SendInputCommandProperty); } set { SetValue(SendInputCommandProperty, value); } } // Using a DependencyProperty as the backing store for MyProperty. This enables animation, styling, binding, etc... public static readonly DependencyProperty SendInputCommandProperty = DependencyProperty.Register("MyProperty", typeof(ICommand), typeof(MyUserControl1), new PropertyMetadata(null)); public MyUserControl1() { this.InitializeComponent(); } }
MyControl1.xaml
<UserControl x:Class="TestBindingSolution.MyUserControl1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:TestBindingSolution" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="400" x:Name="mycontrol1"> <Grid> <Button Content="ttt" Command="{x:Bind SendInputCommand}" /> </Grid>
MainPage.xaml
<Page x:Class="TestBindingSolution.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:TestBindingSolution" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewmodels="using:TestBindingSolution.ViewModels" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" x:Name="MyPage" Loaded="Page_Loaded"> <Page.DataContext> <viewmodels:MainPageViewModel x:Name="ViewModel"/> </Page.DataContext> <StackPanel> <Button Content="ssss" Command="{Binding DataContext.SendInputCommand,ElementName=MyPage}"/> <local:MyUserControl1 SendInputCommand="{x:Bind ViewModel.SendInputCommand}" /> </StackPanel>
MainPageViewModel.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; using TestBindingSolution.Custom; namespace TestBindingSolution.ViewModels { public class MainPageViewModel : Observable { #region Commands public RelayCommand SendInputCommand { get; } #endregion public MainPageViewModel() { this.SendInputCommand = new RelayCommand(this.OnSendInputCommand,this.CanExecuteSendInputCommand); } private bool CanExecuteSendInputCommand(object parameter) { return true; } private void OnSendInputCommand(object parameter) { throw new NotImplementedException(); } } }
补充代码(Observable和RelayCommand)
Observable类
public abstract class Observable : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual bool SetValue<T>(ref T storage, T value, [CallerMemberName] string propertyName = null) { if (EqualityComparer<T>.Default.Equals(storage, value)) return false; storage = value; this.RaisePropertyChanged(propertyName); return true; } public void RaisePropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
RelayCommand类
public class RelayCommand : ICommand { public event EventHandler CanExecuteChanged; private Action<object> _commandAction; private Func<object, bool> _canExecuteFunction; public RelayCommand(Action<object> commandAction) : this(commandAction, null) { } public RelayCommand(Action<object> commandAction, Func<object, bool> canExecuteFunction) { this._commandAction = commandAction; this._canExecuteFunction = canExecuteFunction; } public void RaiseCanExecuteChanged() { this.CanExecuteChanged?.Invoke(this, EventArgs.Empty); } public bool CanExecute(object parameter) { bool returnValue = true; if (this._canExecuteFunction != null) { returnValue = this._canExecuteFunction.Invoke(parameter); } return returnValue; } public void Execute(object parameter) { this._commandAction?.Invoke(parameter); } }
问题根源
核心错误:你在注册依赖属性时,DependencyProperty.Register的第一个参数(依赖属性的唯一标识名称)填成了"MyProperty",但对应的CLR包装属性名是SendInputCommand,两者完全不匹配。
WPF/UWP的Binding系统依赖依赖属性的注册名称来匹配属性,而x:Bind是编译时绑定,直接访问CLR属性,所以不会暴露这个错误。Button的Command是系统内置的依赖属性,名称匹配正确,因此绑定能正常运行。
解决方法
修改SendInputCommandProperty的注册代码,将第一个参数改为与CLR属性一致的"SendInputCommand":
public static readonly DependencyProperty SendInputCommandProperty = DependencyProperty.Register("SendInputCommand", typeof(ICommand), typeof(MyUserControl1), new PropertyMetadata(null));
修改后,使用{Binding DataContext.SendInputCommand, ElementName=MyPage}绑定自定义UserControl的SendInputCommand属性即可正常工作。
内容的提问来源于stack exchange,提问作者Guru98
相关产品推荐
相关产品推荐

