解决DataTemplate绑定附加属性

作者:袖梨 2022-06-25

文 Silverlight 版本:4.0。

首先定义数据类型,此文始终使用此定义类型。
view sourceprint?
1 public class SimpleData : ViewModelBase
2 {
3 private string _text;
4 private int _column, _row;
5
6 public string Text { get { return _text; } set { _text = value; OnPropertyChanged("Text"); } }
7 public int Column { get { return _column; } set { _column = value; OnPropertyChanged("Column"); } }
8 public int Row { get { return _row; } set { _row = value; OnPropertyChanged("Row"); } }
9 }
前台代码:
view sourceprint?
01 Grid x:Name="LayoutRoot" Background="White">
02 ItemsControl ItemsSource="{Binding}">
03 ItemsControl.ItemTemplate>
04 DataTemplate>
05 TextBox Text="{Binding Text}"
06 Foreground="Green"
07 Grid.Row="{Binding Row}"
08 Grid.Column="{Binding Column}"
09 Height="30" Width="150"
10 />
11 DataTemplate>
12 ItemsControl.ItemTemplate>
13 ItemsControl.ItemsPanel>
14 ItemsPanelTemplate>
15 Grid ShowGridLines="True">
16 Grid.RowDefinitions>
17 RowDefinition/>
18 RowDefinition/>
19 RowDefinition/>
20 Grid.RowDefinitions>
21 Grid.ColumnDefinitions>
22 ColumnDefinition/>
23 ColumnDefinition/>
24 Grid.ColumnDefinitions>
25 Grid>
26 ItemsPanelTemplate>
27 ItemsControl.ItemsPanel>
28 ItemsControl>
29 Grid>
后台代码:
view sourceprint?
01 public partial class MainPage : UserControl
02 {
03 public MainPage()
04 {
05 InitializeComponent();
06 this.DataContext = new SimpleData[]
07 {
08 new SimpleData{ Text = "111111", Column = 0, Row = 0 },
09 new SimpleData{ Text = "222222", Column = 1, Row = 1 },
10 new SimpleData{ Text = "333333", Column = 0, Row = 2 },
11 };
12 }
13 }
可以看出这段代码的本意是通过绑定的方式设置,在 ItemsControl 里面显示 3 个 TextBox,同时指定了相应在 Grid 的行和列。
但是,你懂的!
这样的代码肯定是不能正确运行。特别是在Silverlight。
如果这是在 WPF 环境,很庆幸你还可以用 ItemContainerStyle 搞定:
view sourceprint?
1 ItemsControl.ItemContainerStyle>
2 Style>
3 Setter Property="Grid.Row" Value="{Binding Row, Mode=OneWay}"/>
4 Setter Property="Grid.Column" Value="{Binding Column, Mode=OneWay}"/>
5 Style>
6 ItemsControl.ItemContainerStyle>
只可惜这是在 Silverlight 环境。我们只能够想别的办法了。
为什么不可以?拿出 Silverlight Spy 或者 Snoop 查看相应的 VisualTree。可以看到在 TextBox 外面还套了一个 ContextPresenter
于是我们可以想到,能不能设置 ContextPresenter 的 Grid.Row 和 Grid.Colume 达到控制行列的目的?
于是我们得到下面的思路,使用附加属性把相应的绑定关系提升。
view sourceprint?
001 using System;
002 using System.Collections.Generic;
003 using System.Globalization;
004 using System.Linq;
005 using System.Reflection;
006 using System.Windows;
007 using System.Windows.Controls;
008 using System.Windows.Data;
009 using System.Windows.Media;
010
011 namespace Delay
012 {
013 public class UpUp : DependencyObject
014 {
015 // Using a DependencyProperty as the backing store for Up. This enables animation, styling, binding, etc...
016 public static readonly DependencyProperty UpProperty =
017 DependencyProperty.RegisterAttached("Up", typeof(string), typeof(UpUp), new PropertyMetadata(string.Empty));
018
019 public static void SetUp(FrameworkElement element, string value)
020 {
021 HanderClosure hander = element.GetValue(UpProperty) as HanderClosure;
022 if (hander == null)
023 {
024 hander = new HanderClosure(element, value);
025 element.SetValue(UpProperty, value);
026 element.LayoutUpdated += new EventHandler(hander.element_LayoutUpdated);
027 }
028 }
029 public static string GetUp(FrameworkElement element)
030 {
031 HanderClosure hander = element.GetValue(UpProperty) as HanderClosure;
032 if (hander == null)
033 return null;
034 else
035 return hander.OrgParamenter;
036 }
037
038 private class HanderClosure
039 {
040 private FrameworkElement _elem = null;
041 private string[] propertys = null;
042 private int _level;
043 private UpMode _mode;
044 private string _orgParamenter;
045
046 public string OrgParamenter { get { return _orgParamenter; } }
047
048 public HanderClosure(FrameworkElement element, string parameter)
049 {
050 if (element == null)
051 throw new ArgumentNullException("element");
052 if (parameter == null)
053 throw new ArgumentNullException("parameter");
054 _elem = element;
055 _level = 1;
056 _mode = UpMode.Copy;
057 _orgParamenter = parameter;
058
059 string[] array = parameter.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
060 if (array.Length == 0)
061 throw new ArgumentException("parameter");
062 propertys = array[0].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
063 if (array.Length > 1)
064 {
065 int num;
066 if (int.TryParse(array[1].Trim(), out num))
067 {
068 _level = num;
069 }
070 }
071 if (array.Length > 2)
072 {
073 UpMode mode;
074 if (Enum.TryParse(array[2].Trim(), true, out mode))
075 {
076 _mode = mode;
077 }
078 }
079 }
080
081 public void element_LayoutUpdated(object sender, EventArgs e)
082 {
083 FrameworkElement parent = _elem;
084 for (int i = 0; i null; i++)
085 {
086 parent = VisualTreeHelper.GetParent(parent) as FrameworkElement;
087 }
088 if (parent == null)
089 return;
090
091 foreach (string property in propertys)
092 {
093 Apply(_elem, parent, property.Trim());
094 }
095 }
096
097 // Copyright (C) Microsoft Corporation. All Rights Reserved.
098 // This code released under the terms of the Microsoft Public License
099 // (Ms-PL, http://op*e**nsource.org/licenses/ms-pl.html).
100 private void Apply(FrameworkElement element1, FrameworkElement element2, string property)
101 {
102 var array = property.Split('.');
103 if (array.Length != 2)
104 throw new ArgumentException("property");
105 string typeName = array[0].Trim();
106 string propertyName = array[1].Trim();
107
108 Type type = null;
109 foreach (var assembly in AssembliesToSearch)
110 {
111 // Match on short or full name
112 type = assembly.GetTypes()
113 .Where(t => (t.FullName == typeName) || (t.Name == typeName))
114 .FirstOrDefault();
115 if (type != null)
116 break;
117 }
118 if (null == type)
119 {
120 // Unable to find the requested type anywhere
121 throw new ArgumentException(
122 string.Format(
123 CultureInfo.CurrentCulture,
124 "Unable to access type "{0}". Try using an assembly qualified type name.",
125 typeName));
126 &n

相关文章

精彩推荐