Подтвердить что ты не робот

Пользовательский привязчик свойств Silverlight UserControl

Каков правильный способ реализации пользовательских свойств в Silverlight UserControls?

Каждая "страница" в Silverlight технически является UserControl (они получены из класса UserControl). Когда я говорю UserControl здесь, я имею в виду пользовательский UserControl, который будет использоваться на многих разных страницах во многих разных сценариях (аналогично ASP.NET UserControl).

Я бы хотел, чтобы пользовательский UserControl поддерживал привязку и не полагался на Имя свойства, к которому он привязан, чтобы всегда быть тем же. Вместо этого я хотел бы, чтобы сам UserControl имел свойство, к которому привязаны элементы управления внутри UserControl, и к ним также привязаны элементы ViewModels вне UserControl. (см. пример ниже)

Связывание внутри работ UserControl, привязка в MainPage работает, привязка, которую я установил между MainPage и UserControl, не работает. В частности, эта строка:

<myUserControls:MyCustomUserControl x:Name="MyCustomControl2" 
    SelectedText="{Binding MainPageSelectedText, Mode=TwoWay}" 
    Width="200" Height="50" />

Пример вывода:
alt text

MainPage.xaml

<UserControl x:Class="SilverlightCustomUserControl.MainPage"
    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:myUserControls="clr-namespace:SilverlightCustomUserControl"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
    DataContext="{Binding RelativeSource={RelativeSource Self}}">
  <Canvas x:Name="LayoutRoot">
    <StackPanel Orientation="Vertical">
      <TextBlock Text="UserControl Binding:" Width="200"></TextBlock>
      <myUserControls:MyCustomUserControl x:Name="MyCustomControl2" SelectedText="{Binding MainPageSelectedText, Mode=TwoWay}" Width="200" Height="50" />
      <TextBlock Text="MainPage Binding:" Width="200"></TextBlock>
      <TextBox Text="{Binding MainPageSelectedText, Mode=TwoWay}" Width="200"></TextBox>
      <Border BorderBrush="Black" BorderThickness="1">
        <TextBlock Text="{Binding MainPageSelectedText}" Width="200" Height="24"></TextBlock>
      </Border>
    </StackPanel>
  </Canvas>
</UserControl>

MainPage.xaml.cs

namespace SilverlightCustomUserControl
{
 public partial class MainPage : UserControl, INotifyPropertyChanged
 {
  //NOTE: would probably be in a ViewModel
  public string MainPageSelectedText
  {
   get { return _MainPageSelectedText; }
   set
   {
    string myValue = value ?? String.Empty;
    if (_MainPageSelectedText != myValue)
    {
     _MainPageSelectedText = value;
     OnPropertyChanged("MainPageSelectedText");
    }
   }
  }
  private string _MainPageSelectedText;


  public MainPage()
  {
   InitializeComponent();
  }


  #region INotifyPropertyChanged Members

  public event PropertyChangedEventHandler PropertyChanged;

  protected virtual void OnPropertyChanged(string name)
  {
   PropertyChangedEventHandler ph = this.PropertyChanged;

   if (ph != null)
    ph(this, new PropertyChangedEventArgs(name));
  }

  #endregion
 }
}

MyCustomUserControl.xaml

<UserControl
   x:Class="SilverlightCustomUserControl.MyCustomUserControl" 
   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"
   DataContext="{Binding RelativeSource={RelativeSource Self}}">
  <Grid>
    <StackPanel>
      <TextBox Text="{Binding SelectedText, Mode=TwoWay}" />
      <Border BorderBrush="Black" BorderThickness="1">
        <TextBlock Text="{Binding SelectedText}" Height="24"></TextBlock>
      </Border>
    </StackPanel>
  </Grid>
</UserControl>

MyCustomUserControl.xaml.cs

namespace SilverlightCustomUserControl
{
 public partial class MyCustomUserControl : UserControl
 {
  public string SelectedText
  {
   get { return (string)GetValue(SelectedTextProperty); }
   set { SetValue(SelectedTextProperty, value); }
  }

  public static readonly DependencyProperty SelectedTextProperty =
    DependencyProperty.Register("SelectedText", typeof(string), typeof(MyCustomUserControl), new PropertyMetadata("", SelectedText_PropertyChangedCallback));


  public MyCustomUserControl()
  {
   InitializeComponent();
  }

  private static void SelectedText_PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
   //empty
  }
 }
}

Ссылки (как я догадался):

использовать DependencyPropertys: http://geekswithblogs.net/thibbard/archive/2008/04/22/wpf-custom-control-dependency-property-gotcha.aspx

используйте DependencyPropertys, добавьте x: Name в свой UserControl - добавьте Binding с ElementName, снова установите свойство Custom в методе PropertyChangedCallback: Настройка пользовательских свойств в UserControl через DataBinding

не использовать пользовательские свойства, полагаться на базовые имена datacontext (мне не нравится это решение): wpf проблема с использованием свойств зависимостей в UserControl

4b9b3361

Ответ 1

Я понимаю, что причина, по которой ваш элемент управления не получает новое значение с страницы мщения, заключается в том, что вы устанавливаете DataContext элемента управления. Если бы вы этого не сделали, тогда контроль DataContext будет унаследован от его родителя, главной страницы в этом случае.

Чтобы заставить это работать, я удалил элемент управления DataContext, добавил x: Name для каждого элемента управления и установил привязку в конструкторе элемента управления с помощью метода [name].SetBinding.

Я сделал привязку в ctor, так как не мог найти способ установить свойство Source декларативного привязки в xaml для Self. т.е. {Binding SelectedText, Mode = TwoWay, Source = [Self here some how]}. Я попытался использовать RelativeSource = {RelativeSource Self} без радости.

ПРИМЕЧАНИЕ. Все это SL3.

Ответ 2

Проблема заключалась в том, что UserControl выбрасывал ошибку DataBinding (видимо в окне "Вывод" при отладке)

Поскольку для DataContext UserControl был установлен "Self" в своем собственном xaml, он искал MainPageSelectedText в своем собственном контексте (он не искал MainPageSelectedText в "MainPage", где вы могли бы подумать, что он будет выглядеть, потому что когда вы физически пишете/смотрите код, который находится в "контексте" )

Мне удалось получить эту "работу", установив привязку в коде. Установка привязки в коде позади - единственный способ установить сам UserControl как "Источник" привязки. Но это работает, только если привязка - TwoWay. Связывание OneWay нарушит этот код. Лучшим решением в целом было бы создание Silverlight Control, а не UserControl.

См. также:

http://social.msdn.microsoft.com/Forums/en-US/silverlightcontrols/thread/052a2b67-20fc-4f6a-84db-07c85ceb3303

http://msdn.microsoft.com/en-us/library/cc278064%28VS.95%29.aspx

MyCustomUserControl.xaml

<UserControl
   x:Class="SilverlightCustomUserControl.MyCustomUserControl" 
 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">
 <Grid>
  <StackPanel>
   <TextBox x:Name="UserControlTextBox" />
   <Border BorderBrush="Black" BorderThickness="1">
    <TextBlock x:Name="UserControlTextBlock" Height="24"></TextBlock>
   </Border>
  </StackPanel>
 </Grid>
</UserControl>

MyCustomUserControl.xaml.cs

namespace SilverlightCustomUserControl
{
 public partial class MyCustomUserControl : UserControl
 {

  public string SelectedText
  {
   get { return (string)GetValue(SelectedTextProperty); }
   set { SetValue(SelectedTextProperty, value); }
  }

  public static readonly DependencyProperty SelectedTextProperty =
    DependencyProperty.Register("SelectedText", typeof(string), typeof(MyCustomUserControl), new PropertyMetadata("", SelectedText_PropertyChangedCallback));


  public MyCustomUserControl()
  {
   InitializeComponent();

               //SEE HERE
   UserControlTextBox.SetBinding(TextBox.TextProperty, new Binding() { Source = this, Path = new PropertyPath("SelectedText"), Mode = BindingMode.TwoWay });
   UserControlTextBlock.SetBinding(TextBlock.TextProperty, new Binding() { Source = this, Path = new PropertyPath("SelectedText") });
               //SEE HERE
  }

  private static void SelectedText_PropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
   //empty
  }

 }
}

Ответ 3

Вместо привязки контекста данных к себе вы можете установить привязку в xaml, добавив x:Name для пользовательского элемента управления, а затем привязка в пользовательском элементе управления xaml следует:

<UserControl
  x:Class="SilverlightCustomUserControl.MyCustomUserControl" 
  x:Name="myUserControl
  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">
    <Grid>
        <StackPanel>
            <TextBox Text="{Binding SelectedText, ElementName=myUserContol, Mode=TwoWay}" />
            <Border BorderBrush="Black" BorderThickness="1">
                <TextBlock Text="{Binding SelectedText,ElementName=myUserControl}" Height="24"></TextBlock>
            </Border>
        </StackPanel>
     </Grid>
</UserControl>