将 Silverlight UserControl 自定义属性绑定到其元素
Posted
技术标签:
【中文标题】将 Silverlight UserControl 自定义属性绑定到其元素【英文标题】:Binding Silverlight UserControl custom properties to its' elements 【发布时间】:2010-10-18 07:10:22 【问题描述】:我正在尝试在 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>
我还在 Square 类中创建了 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 中这样做会更优雅。有谁知道这是怎么做到的?
【问题讨论】:
如此重要的问题却又如此难以捉摸的答案。 【参考方案1】:我可能没有完全理解您的问题。在 Silverlight 中,您几乎可以绑定到任何数据对象。因此,如果您有一个包含属性 Content 和 Label 的 PuzzleSquare 类,您可以直接从对象绑定到这些属性。
假设您创建了一个简单的对象 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" />
这有意义吗?
ib.
【讨论】:
有道理,但我的情况不同。在您的情况下,PuzzleSquare 和 UI 是单独的类。我还没有那么远。我仍在定义我的 UI 类。我想向绑定到子元素属性的 UI 类添加一个公共属性。然后我将数据绑定到 UI 的 public 属性上。 那里的字符用完了。所以在我的设计中,我将像这样使用我的类我认为您正在寻找 UI Element to Element Binding,这是 Silverlight 3 的一个功能。
【讨论】:
正如 James Cadd 所说:如果您在应用程序中使用 UserControl 并为其命名,则会中断。【参考方案3】:首先,使用 RelativeSource Self 在 UserControl 上设置 DataContext:
<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">
现在您可以将单个元素绑定到用户控件的属性:
<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,您需要在 UserControl 的 Loaded 事件处理程序上设置 DataContext。
private void UserControl_Loaded( object sender, RoutedEventArgs e )
LayoutRoot.DataContext = this;
【讨论】:
这不正是 MVVM 良好实践的反面吗?在 MVVM 世界中,UserControl 的 DataContext 应该设置为 ViewModel。如果我想将一个控件绑定到 ViewModel,而将另一个控件绑定到我的 UserControl 的属性怎么办? 我同意上面的评论——除非我遗漏了一些东西,否则这会破坏数据上下文的有用性,因为假设你在组件中绑定的所有内容都将在控件中。除了绑定到 LayoutRoot 父级的技巧之外,我在 xaml 中看不到任何合理的方法来执行此操作,因此我只需要为元素命名并在依赖属性的代码隐藏中初始化它们。【参考方案4】:这在 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"/>
【讨论】:
如果您在应用程序中使用 UserControl 并为其命名,则会中断。【参考方案5】:由于 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 中得到的相同问题的答案并没有说服我。
【讨论】:
在此页面上的解决方案中,这似乎是唯一有效的解决方案,但是这很尴尬,我将在代码隐藏中按名称初始化我的东西。【参考方案6】:试试这个:
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 和代码后面绑定。
【讨论】:
以上是关于将 Silverlight UserControl 自定义属性绑定到其元素的主要内容,如果未能解决你的问题,请参考以下文章
Silverlight UserControl 自定义属性绑定
何时在 Silverlight 中使用 UserControl 与 Control?
x:如果元素包含在UserControl的内容中(Silverlight),则名称不起作用
Silverlight Xaml 覆盖控件的 IsEnabled 属性