【Silverlight】解决DataTemplate绑定附加属性
??? 本文 Silverlight 版本:4.0。
??? 首先定義數據類型,此文始終使用此定義類型。
public class SimpleData : ViewModelBase{private string _text;private int _column, _row;public string Text { get { return _text; } set { _text = value; OnPropertyChanged("Text"); } }public int Column { get { return _column; } set { _column = value; OnPropertyChanged("Column"); } }public int Row { get { return _row; } set { _row = value; OnPropertyChanged("Row"); } }}??? 前臺代碼:
<Grid x:Name="LayoutRoot" Background="White"><ItemsControl ItemsSource="{Binding}"><ItemsControl.ItemTemplate><DataTemplate><TextBox Text="{Binding Text}"Foreground="Green"Grid.Row="{Binding Row}"Grid.Column="{Binding Column}"Height="30" Width="150"/></DataTemplate></ItemsControl.ItemTemplate><ItemsControl.ItemsPanel><ItemsPanelTemplate><Grid ShowGridLines="True"><Grid.RowDefinitions><RowDefinition/><RowDefinition/><RowDefinition/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions></Grid></ItemsPanelTemplate></ItemsControl.ItemsPanel></ItemsControl></Grid>??? 后臺代碼:
public partial class MainPage : UserControl{public MainPage(){InitializeComponent();this.DataContext = new SimpleData[]{new SimpleData{ Text = "111111", Column = 0, Row = 0 },new SimpleData{ Text = "222222", Column = 1, Row = 1 }, new SimpleData{ Text = "333333", Column = 0, Row = 2 }, };}}??? 可以看出這段代碼的本意是通過綁定的方式設置,在 ItemsControl 里面顯示 3 個 TextBox,同時指定了相應在 Grid 的行和列。
??? 但是,你懂的!
??? 這樣的代碼肯定是不能正確運行。特別是在Silverlight。
??? 如果這是在 WPF 環境,很慶幸你還可以用 ItemContainerStyle 搞定:
<ItemsControl.ItemContainerStyle><Style><Setter Property="Grid.Row" Value="{Binding Row, Mode=OneWay}"/><Setter Property="Grid.Column" Value="{Binding Column, Mode=OneWay}"/></Style></ItemsControl.ItemContainerStyle>??? 只可惜這是在 Silverlight 環境。我們只能夠想別的辦法了。
?
??? 為什么不可以?拿出 Silverlight Spy 或者 Snoop 查看相應的 VisualTree。可以看到在 TextBox 外面還套了一個 ContextPresenter。
??? 于是我們可以想到,能不能設置 ContextPresenter 的 Grid.Row 和 Grid.Colume 達到控制行列的目的?
??? 于是我們得到下面的思路,使用附加屬性把相應的綁定關系提升。
using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Reflection; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Media;namespace Delay {public class UpUp : DependencyObject{// Using a DependencyProperty as the backing store for Up. This enables animation, styling, binding, etc...public static readonly DependencyProperty UpProperty =DependencyProperty.RegisterAttached("Up", typeof(string), typeof(UpUp), new PropertyMetadata(string.Empty));public static void SetUp(FrameworkElement element, string value){HanderClosure hander = element.GetValue(UpProperty) as HanderClosure;if (hander == null){hander = new HanderClosure(element, value);element.SetValue(UpProperty, value);element.LayoutUpdated += new EventHandler(hander.element_LayoutUpdated);}}public static string GetUp(FrameworkElement element){HanderClosure hander = element.GetValue(UpProperty) as HanderClosure;if (hander == null)return null;elsereturn hander.OrgParamenter;}private class HanderClosure{private FrameworkElement _elem = null;private string[] propertys = null;private int _level;private UpMode _mode;private string _orgParamenter;public string OrgParamenter { get { return _orgParamenter; } }public HanderClosure(FrameworkElement element, string parameter){if (element == null)throw new ArgumentNullException("element");if (parameter == null)throw new ArgumentNullException("parameter");_elem = element;_level = 1;_mode = UpMode.Copy;_orgParamenter = parameter;string[] array = parameter.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);if (array.Length == 0)throw new ArgumentException("parameter");propertys = array[0].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);if (array.Length > 1){int num;if (int.TryParse(array[1].Trim(), out num)){_level = num;}}if (array.Length > 2){UpMode mode;if (Enum.TryParse<UpMode>(array[2].Trim(), true, out mode)){_mode = mode;}}}public void element_LayoutUpdated(object sender, EventArgs e){FrameworkElement parent = _elem;for (int i = 0; i < _level && parent != null; i++){parent = VisualTreeHelper.GetParent(parent) as FrameworkElement;}if (parent == null)return;foreach (string property in propertys){Apply(_elem, parent, property.Trim());}}// Copyright (C) Microsoft Corporation. All Rights Reserved.// This code released under the terms of the Microsoft Public License// (Ms-PL, http://opensource.org/licenses/ms-pl.html).private void Apply(FrameworkElement element1, FrameworkElement element2, string property){var array = property.Split('.');if (array.Length != 2)throw new ArgumentException("property");string typeName = array[0].Trim();string propertyName = array[1].Trim();Type type = null;foreach (var assembly in AssembliesToSearch){// Match on short or full nametype = assembly.GetTypes().Where(t => (t.FullName == typeName) || (t.Name == typeName)).FirstOrDefault();if (type != null)break;}if (null == type){// Unable to find the requested type anywherethrow new ArgumentException(string.Format(CultureInfo.CurrentCulture,"Unable to access type \"{0}\". Try using an assembly qualified type name.",typeName));}// Get the DependencyProperty for which to set the BindingDependencyProperty dp = null;var field = type.GetField(propertyName + "Property",BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static);if (null != field){dp = field.GetValue(null) as DependencyProperty;}if (null == dp){// Unable to find the requsted propertythrow new ArgumentException(string.Format(CultureInfo.CurrentCulture,"Unable to access DependencyProperty \"{0}\" on type \"{1}\".",propertyName,type.Name));}BindingExpression binding = element1.GetBindingExpression(dp);object value = element1.GetValue(dp);if (binding != null){element2.SetBinding(dp, binding.ParentBinding);}else if (value != null){element2.SetValue(dp, value);}if (_mode == UpMode.Move)element1.ClearValue(dp);}// Copyright (C) Microsoft Corporation. All Rights Reserved.// This code released under the terms of the Microsoft Public License// (Ms-PL, http://opensource.org/licenses/ms-pl.html)./// <summary>/// Gets a sequence of assemblies to search for the provided type name./// </summary>private IEnumerable<Assembly> AssembliesToSearch{get{// Start with the System.Windows assembly (home of all core controls)yield return typeof(Control).Assembly;#if SILVERLIGHT && !WINDOWS_PHONE// Fall back by trying each of the assemblies in the Deployment's Parts listforeach (var part in Deployment.Current.Parts){var streamResourceInfo = Application.GetResourceStream(new Uri(part.Source, UriKind.Relative));using (var stream = streamResourceInfo.Stream){yield return part.Load(stream);}} #endif}}}private enum UpMode{Move,Copy,}} }??? 如何使用?使用非常簡單!
??? 在你的項目中增加 UpUp 之后,在需要提升綁定級別的 Page 的 Xaml 中引入命名空間 xmlns:delay="clr-namespace:Delay"。然后在需要提升綁定級別的控件中加入屬性 delay:UpUp.Up="Grid.Row,Grid.Column"。得到完整的前臺代碼如下:
<UserControl x:Class="TestValueBindingInItemTemplate.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:delay="clr-namespace:Delay"mc:Ignorable="d"d:DesignHeight="300" d:DesignWidth="400"><Grid x:Name="LayoutRoot" Background="White"><ItemsControl ItemsSource="{Binding}"><ItemsControl.ItemTemplate><DataTemplate><TextBox Text="{Binding Text}"Foreground="Green"Grid.Row="{Binding Row}"Grid.Column="{Binding Column}"Height="30" Width="150"delay:UpUp.Up="Grid.Row,Grid.Column"/></DataTemplate></ItemsControl.ItemTemplate><ItemsControl.ItemsPanel><ItemsPanelTemplate><Grid ShowGridLines="True"><Grid.RowDefinitions><RowDefinition/><RowDefinition/><RowDefinition/></Grid.RowDefinitions><Grid.ColumnDefinitions><ColumnDefinition/><ColumnDefinition/></Grid.ColumnDefinitions></Grid></ItemsPanelTemplate></ItemsControl.ItemsPanel></ItemsControl></Grid> </UserControl>??? UpUp.Up 應該如何填寫?實際上 UpUp.Up 屬性有具體的語法格式:
?
UpUp.Up="Type.Property[,Type.Property ...][;Level[;Move|Copy]]"
?
??? 其中
??? Type.Property 是需要提升綁定關系的屬性名稱,可以用逗號把多個屬性名稱隔開。
??? Level 是整數,表示需要提升的層次。在 VisualTree 中向上一層為一個層次。
??? Move|Copy 是枚舉類型,表示提升之后保留原來的綁定關系。
??? 例如:delay:UpUp.Up="Grid.Row,Grid.Column;1;Copy"
?
??? 有了 UpUp 之后,對于類似的綁定問題可以輕而易舉的完成了!
?
PS:WPF 也可以用此方法實現,但是有細節方面的差異。
1、不能夠使用SetXXX GetXXX,要使用 XXX 屬性。
2、需要注冊 PropertyChangedCallback 事件,并將相關注冊 Hander 部分放置到此方法內。
?
本文完整代碼在此下載:http://files.cnblogs.com/Aimeast/SLTestValueBindingInItemTemplate.zip
轉載于:https://www.cnblogs.com/Aimeast/archive/2011/09/11/2173788.html
總結
以上是生活随笔為你收集整理的【Silverlight】解决DataTemplate绑定附加属性的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何在CentOS 5.5上安装Kipp
- 下一篇: 致年轻开发人员的一封信