Привязка пользовательских свойств Silverlight UserControl - PullRequest
16 голосов
/ 06 октября 2009

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

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

Я бы хотел, чтобы Custom 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 через привязку данных

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

Ответы [ 3 ]

7 голосов
/ 09 октября 2009

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

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

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

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

4 голосов
/ 20 октября 2009

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

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

Я смог заставить это "работать", установив Binding в коде. Установка привязки в коде - единственный способ установить сам UserControl в качестве «источника» привязки. Но это работает, только если Binding является двухсторонним. Привязка OneWay нарушит этот код. Лучше всего было бы создать элемент управления Silverlight , а не 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
  }

 }
}
0 голосов
/ 10 марта 2014

Вместо привязки контекста данных к себе, вы можете установить привязку в 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>
...