Связывание пользовательских свойств Silverlight UserControl с его элементами

17

Я пытаюсь создать простую кроссвордную игру в Silverlight 2.0. Я работаю над компонентом UserControl-ish, который представляет собой квадрат в головоломке. У меня возникли проблемы с привязкой свойств UserControl с его элементами. Я наконец-то (вроде) получил его работу (может быть полезно для некоторых - мне потребовалось несколько долгих часов), но хотелось сделать его более «элегантным».

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

    <UserControl x:Class="XWord.Square"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    FontSize="30" 
    Width="100" Height="100">
        <Grid x:Name="LayoutRoot" Background="White">
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>

            <TextBlock x:Name="Label" Grid.Row="0" Grid.Column="1" 
                Text="7"/>
            <TextBox x:Name="Content" Grid.Row="1" Grid.Column="0"  
                Text="A"
                BorderThickness="0" />

        </Grid>
    </UserControl>

Я также создал DependencyProperties в классе Square следующим образом:

     public static readonly DependencyProperty LabelTextProperty;
     public static readonly DependencyProperty ContentCharacterProperty;

     // ...(static constructor with property registration, .NET properties
     // omitted for brevity)...

Теперь я хотел бы выяснить, как привязать элемент Label и Content к двум свойствам. Я делаю это так (в файле с кодом):

     Label.SetBinding( TextBlock.TextProperty, new Binding { Source = this, Path = new PropertyPath( "LabelText" ), Mode = BindingMode.OneWay } );
     Content.SetBinding( TextBox.TextProperty, new Binding { Source = this, Path = new PropertyPath( "ContentCharacter" ), Mode = BindingMode.TwoWay } );

Это было бы более элегантно сделано в XAML. Кто-нибудь знает, как это делается?

    
задан Ronald Zarīts 08.04.2009 в 14:15
источник
  • Такой важный вопрос пока такой неуловимый ответ. –  Scott Willeke 07.12.2009 в 21:51

6 ответов

19

Сначала установите DataContext в UserControl с помощью {RelativeSource Self}:

<UserControl x:Class="XWord.Square"  
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"   
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
FontSize="30"   
Width="100" Height="100" 
DataContext="{Binding RelativeSource={RelativeSource Self}}">

Теперь вы можете привязать отдельные элементы к свойствам usercontrol:

<TextBlock x:Name="Label" Grid.Row="0" Grid.Column="1" 
Text="{Binding LabelText}"/>  
<TextBox x:Name="Content" Grid.Row="1" Grid.Column="0" 
Text="{Binding ContentCharacter}" BorderThickness="0" />

Для SL 2.0 вам необходимо установить DataContext в обработчике событий Loaded UserControl.

private void UserControl_Loaded( object sender, RoutedEventArgs e ) {
    LayoutRoot.DataContext = this;
}
    
ответ дан lo5 26.04.2010 в 10:22
  • Разве это не просто противоположный способ успешной практики MVVM? В мире MVVM DataContext UserControl должен быть установлен в ViewModel. Что делать, если я хочу привязать элемент управления к ViewModel, а другой - к свойству UserControl? –  JYL 11.11.2011 в 14:17
  • Я согласен с приведенным выше комментарием - если я не пропущу что-то, это разрушает полезность datacontext, предполагая, что все, с чем вы связываетесь в компоненте, будет находиться в элементе управления. Помимо трюка привязки к родительскому элементу LayoutRoot, я не вижу разумного способа сделать это в xaml, поэтому я прибегаю к тому, чтобы просто дать элементам имя и инициализировать их в моем коде из свойства зависимостей. –  Pat Niemeyer 25.02.2012 в 21:43
7

Поскольку Silverlight не может использовать технику FindAncestor, вы можете использовать трюк, подобный тому, который устанавливает имя UserControl, но не нарушая его функциональность, используя имя LayoutRoot ...

<UserControl x:Class="XWord.Square"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
FontSize="30" 
Width="100" Height="100">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="Auto"/>
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <TextBlock x:Name="{Binding Path=Parent.LabelText, ElementName=LayoutRoot}" Grid.Row="0" Grid.Column="1" 
            Text="7"/>
        <TextBox x:Name="{Binding Path=Parent.ContentCharacter, ElementName=LayoutRoot}" Grid.Row="1" Grid.Column="0"  
            Text="A"
            BorderThickness="0" />
    </Grid>
</UserControl>

Он работал в SL3 без необходимости добавлять дополнительный код (я использую его в приложении WP7), но не знаю, можете ли вы его использовать в SL2. Ну, теперь я понимаю, как этот вопрос старый, надеюсь, что он по-прежнему полезен, я приехал сюда, потому что ответы, которые я получил по той же проблеме в WP7, не убедили меня.

    
ответ дан jmservera 23.04.2011 в 19:58
  • Из решений на этой странице это похоже на единственно допустимый, однако достаточно неудобно, что я собираюсь просто инициализировать мои вещи в кодебе по имени. –  Pat Niemeyer 25.02.2012 в 21:44
2

Я думаю, что вы ищете Элемент интерфейса пользователя к привязке элементов , который является особенностью Silverlight 3.

    
ответ дан Robert Kozak 23.04.2009 в 00:38
  • Как сказал Джеймс Кэдд: Это ломается, если вы потребляете UserControl в приложении и даете ему имя. –  Dänu 07.11.2010 в 16:15
1

Возможно, я не понимаю вашу проблему. В Silverlight вы можете привязываться практически к любому объекту данных. Итак, если у вас есть класс PuzzleSquare, который содержит свойства Content и Label, вы можете привязываться к этим свойствам непосредственно из объекта.

Предположим, вы создали простой объект PuzzleSquare:

    public class PuzzleSquare
    {
      public string Content{ get; set; }
      public string Label{ get; set; }

      public void PuzzleSquare(){};
      public void PuzzleSquare(string label, string content):this()
      {
         Content = content;
         Label = label;
      }    
    }

Итак, если вы создаете приложение с классическим представлением / кодом за моделью, ваш код позади добавит этот объект к свойству DataContext сетки при загрузке страницы:

LayoutRoot.DataContext = new PuzzleSquare("1", "A");

Ваш Xaml будет привязан к свойству Square:

    <TextBlock x:Name="Label" Grid.Row="0" Grid.Column="1" 
Text="{Binding Label}"/>            
    <TextBox x:Name="Content" Grid.Row="1" Grid.Column="0" 
Text="{Binding Content}" BorderThickness="0" />

Это имеет смысл?

И.Б..

    
ответ дан Ireney Berezniak 08.04.2009 в 19:48
  • Это имеет смысл, но мой сценарий отличается. В вашем случае PuzzleSquare и пользовательский интерфейс являются отдельными классами. Я еще не так далеко. Я все еще определяю свой класс пользовательского интерфейса. Я хочу добавить публичное свойство в свой UI-класс, который связывается с свойством подэлемента. Затем я привяжу данные к общедоступной настройке пользовательского интерфейса. –  Ronald Zarīts 08.04.2009 в 22:13
  • Ran из символов там .. Так что в моем дизайне я буду использовать свой класс следующим образом <s: SomeCustPnl DataContext = {StaticResource someDataInstance}> <DataTemplate> <s: Square Content="{Binding contentProp}" /> .. Однако мне нужно связать свойство Content с подэлементом Square. Имеет ли это смысл? –  Ronald Zarīts 08.04.2009 в 22:20
0

Это работало в Silverlight 4.0

Поместите имя в UserControl, а затем обратитесь к нему в TextBlock

 <UserControl x:Class="XWord.Square"
    ...omitted for brevity ...
    x:Name="Square">

        <TextBlock x:Name="Label" ...
            Text="{Binding Path=LabelText,ElementName=Square}"/>
    
ответ дан Chui Tey 09.07.2010 в 13:09
  • Это прерывается, если вы потребляете UserControl в приложении и даете ему имя. –  James Cadd 14.09.2010 в 18:24
  • Но все-таки решение –  Ievgen Naida 03.12.2010 в 11:54
0

Попробуйте следующее:

Public ReadOnly TextProperty As DependencyProperty = DependencyProperty.Register("Text", GetType(String), GetType(ButtonEdit), New System.Windows.PropertyMetadata("", AddressOf TextPropertyChanged))
Public Property Text As String
    Get
        Return GetValue(TextProperty)
    End Get
    Set(ByVal value As String)
        SetValue(TextProperty, value)
    End Set
End Property
Private Sub TextPropertyChanged()
    If String.IsNullOrEmpty(Text) Then
        TextBox1.Text = ""
    Else
        TextBox1.Text = Text
    End If
End Sub
Private Sub TextBox1_LostFocus(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles TextBox1.LostFocus
    Text = TextBox1.Text
End Sub

Я могу связать как в XAML, так и в коде.

    
ответ дан SteveC 27.09.2011 в 22:05