2013-08-05 153 views
0

在自定義用戶控件上使用DataBinding時出現一個奇怪的問題。 我的UserControl「UserControl1」有一個依賴項屬性LabelText,它在我的UserControl1中設置一個標籤的內容。此外,它還有一個綁定命令「MyCommand」的按鈕。該命令僅顯示一個消息框並在UserControl1ViewModel中實現。我想在MainWindow.xaml中使用綁定到LabelTextFromMainWindow來設置UserControl的LabelText屬性,但是當我這樣做時,我遇到了一個問題它會使用錯誤的DataContext,除非您明確指定它。自定義用戶控件DataBinding問題

這是我的代碼:

public partial class MainWindow : Window 
{ 
    private MainWindowViewModel vm; 

    public MainWindow() 
    { 
     InitializeComponent(); 

     DataContext = vm = new MainWindowViewModel(); 

     vm.LabelTextFromMainWindow = "Hallo"; 
    } 
} 

class MainWindowViewModel : System.ComponentModel.INotifyPropertyChanged 
{ 
    #region INotifyPropertyChanged Members 

    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; 

    private void OnPropertyChanged(string propertyName) 
    { 
     if (PropertyChanged != null) 
      PropertyChanged(this, 
       new System.ComponentModel.PropertyChangedEventArgs(propertyName)); 
    } 

    #endregion 

    private string myLabel; 

    public string LabelTextFromMainWindow 
    { 
     get { return myLabel; } 
     set 
     { 
      myLabel = value; 
      OnPropertyChanged("MyLabel"); 
     } 
    } 
} 

///////// 

<UserControl x:Class="WpfApplication1.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="224" d:DesignWidth="300"> 
    <Grid> 
     <Button Command="{Binding MyCommand}" Content="Button" Height="55" HorizontalAlignment="Left" Margin="166,99,0,0" Name="button1" VerticalAlignment="Top" Width="104" /> 
     <Label Margin="30,99,0,0" Name="label1" Height="55" VerticalAlignment="Top" HorizontalAlignment="Left" Width="101" /> 
    </Grid> 
</UserControl> 

public partial class UserControl1 : UserControl 
{ 
    private UserControl1ViewModel vm; 

    private static UserControl1 instance; 

    public UserControl1() 
    { 
     InitializeComponent(); 

     instance = this; 

     DataContext = vm = new UserControl1ViewModel(); 
    } 

    public string LabelText 
    { 
     get { return (string)GetValue(LabelProperty); } 
     set { SetValue(LabelProperty, value); } 
    } 

    public static readonly DependencyProperty LabelProperty = 
     DependencyProperty.Register("LabelText", typeof(string), typeof(UserControl1), new UIPropertyMetadata(""), OnValidateValueProperty); 

    private static bool OnValidateValueProperty(object source) 
    { 
     if (instance != null) 
     { 
      instance.label1.Content = source; 
     } 

     return true; 
    } 
} 

public class UserControl1ViewModel 
{ 
    private DelegateCommand myCommand; 

    public ICommand MyCommand 
    { 
     get 
     { 
      if (myCommand == null) 
       myCommand = new DelegateCommand(new Action<object>(MyExecute), 
        new Predicate<object>(MyCanExecute)); 
      return myCommand; 
     } 
    } 

    private bool MyCanExecute(object parameter) 
    { 
     return true; 
    } 

    private void MyExecute(object parameter) 
    { 
     MessageBox.Show("Hello World"); 
    } 
} 

我的主窗口日誌如下:

<Window x:Class="WpfApplication1.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="MainWindow" Height="350" Width="525" 
    xmlns:my="clr-namespace:WpfApplication1"> 
    <Grid> 
     <my:UserControl1 LabelText="{Binding 
           Path=DataContext.LabelTextFromMainWindow, 
           RelativeSource={RelativeSource FindAncestor, 
           AncestorType={x:Type Window}}}" 
         HorizontalAlignment="Left" 
         Margin="114,36,0,0" 
         x:Name="userControl11" 
         VerticalAlignment="Top" Height="236" Width="292" /> 
    </Grid> 
</Window> 

我exspected以下才能正常工作。

LabelText="{Binding Path=LabelTextFromMainWindow}" 

但是,我必須寫這一個。

LabelText="{Binding Path=DataContext.LabelTextFromMainWindow, 
       RelativeSource={RelativeSource FindAncestor, 
             AncestorType={x:Type Window}}}" 

爲了讓簡單的綁定正常工作,我需要做些什麼?

回答

1

默認情況下,控制inherits DataContext from its parent,除非您將其設置爲explicitly。 在你的情況,明確設置你的用戶控件的DataContext的是

DataContext = vm = new UserControl1ViewModel(); 

這使得所有你的用戶控件綁定到查找綁定在UserControl1ViewModel類,而不是MainWindowViewModel

這就是爲什麼你必須使用RelativeSource來獲取Window的DataContext,即you explicitly asked binding to be found in window's DataContext,而不是在它自己的DataContext中,並且我在使用RelativeSource時看不到任何問題。

但是,如果您想像不使用RelativeSource的簡單綁定,首先you need to get rid of explicitly setting DataContext並移動MainWindowsViewModel中的所有命令和屬性,以便您的UserControl從MainWindow繼承它的DataContext。

OR

你可以給名稱到你的窗口和使用ElementName綁定 -

<Window x:Class="WpfApplication1.MainWindow" 
     x:Name="MainWindow"> <--- HERE 
<Grid> 
     <my:UserControl1 LabelText="{Binding 
             Path=DataContext.LabelTextFromMainWindow, 
             ElementName=MainWindow}"/> 
+0

感謝你的解釋,我發現了一個更好的解決方案自己。如果我的UserControl1沒有DataContext,但第一個子項(在這種情況下是名爲「DataContextHolder」的Grid),我可以得到我在找的東西。 <用戶控件X:類= 「WpfApplication1.UserControl1」> \t <網格X:名稱= 「DataContextHolder」> \t 公共部分類的UserControl1:用戶控件 { \t公共的UserControl1() \t { \t \t DataContextHolder。DataContext = new UserControl1ViewModel(); \t} } – BikingGlobetrotter

+0

是的,這是一種破解,但足夠你的問題陳述。 –