2009-04-08 8 views
17

Я пытаюсь создать простую кроссвордную игру в Silverlight 2.0. Я работаю над компонентом UserControl-ish, который представляет собой квадрат в головоломке. У меня возникли проблемы с привязкой свойств UserControl с его элементами. Я наконец-то (вроде) получил его работу (может быть полезно для некоторых - мне потребовалось несколько долгих часов), но хотелось сделать его более «элегантным».Связывание Silverlight Пользовательские свойства 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 в площади класса, как это:

 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. Кто-нибудь знает, как это делается?

+3

Такой важный вопрос пока такой неуловимый ответ. –

ответ

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 привяжет к площади объекта недвижимости:

<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" /> 

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

ib.

+1

Это имеет смысл, но мой сценарий отличается. В вашем случае PuzzleSquare и пользовательский интерфейс являются отдельными классами. Я еще не так далеко. Я все еще определяю свой класс пользовательского интерфейса. Я хочу добавить публичное свойство в свой UI-класс, который связывается с свойством подэлемента. Затем я привяжу данные к общедоступной настройке пользовательского интерфейса. –

+0

Ran out of chars there .. Так что в моем дизайне я буду использовать свой класс так: .. Однако мне нужно связать свойство Content с подэлементом Square. Имеет ли это смысл? –

2

Я думаю, что вы ищете UI Element to Element Binding, который является особенностью Silverlight 3.

+0

Как сказал Джеймс Кэдд: Это ломается, если вы потребляете UserControl в приложении и даете ему имя. –

19

Во-первых, установить DataContext на UserControl с помощью {RelativeSource Атман}:

<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 на нагруженной обработчик события UserControl в ,

private void UserControl_Loaded(object sender, RoutedEventArgs e) { 
    LayoutRoot.DataContext = this; 
} 
+0

Разве это не просто противоположный способ использования MVVM? В мире MVVM DataContext UserControl должен быть установлен в ViewModel. Что делать, если я хочу привязать элемент управления к ViewModel, а другой - к свойству UserControl? – JYL

+0

Я согласен с вышеприведенным комментарием - если я не пропущу что-то, это разрушает полезность datacontext, предполагая, что все, что вы связываете в компоненте, будет находиться в элементе управления. Помимо трюка привязки к родительскому элементу LayoutRoot, я не вижу разумного способа сделать это в xaml, поэтому я прибегаю к тому, чтобы просто дать элементам имя и инициализировать их в моем коде из свойства зависимостей. –

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}"/> 
+1

Это прерывается, если вы потребляете UserControl в приложении и даете ему имя. –

+0

Но все-таки решение ... – Evgeny

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, не убедили меня.

+0

Из решений на этой странице это похоже на единственно верное, однако достаточно неудобно, что я собираюсь просто инициализировать мои вещи в кодебе по имени. –

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 и код позади.