Silverlight】解决DataTemplate绑定附加属性

本文 Silverlight 版本:4.0。

    首先定义数据类型,此文始终使用此定义类型。

1public 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}

    前台代码:

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>

    后台代码:

01public 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 搞定:

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

201109112135557404.png

    于是我们可以想到,能不能设置 ContextPresenter 的 Grid.Row 和 Grid.Colume 达到控制行列的目的?

    于是我们得到下面的思路,使用附加属性把相应的绑定关系提升。

001using System;
002using System.Collections.Generic;
003using System.Globalization;
004using System.Linq;
005using System.Reflection;
006using System.Windows;
007using System.Windows.Controls;
008using System.Windows.Data;
009using System.Windows.Media;
010  
011namespace 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<UpMode>(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 < _level && parent != 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://opensource.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                }
127  
128                // Get the DependencyProperty for which to set the Binding
129                DependencyProperty dp = null;
130                var field = type.GetField(
131                    propertyName + "Property",
132                    BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.Static);
133                if (null != field)
134                {
135                    dp = field.GetValue(null) as DependencyProperty;
136                }
137                if (null == dp)
138                {
139                    // Unable to find the requsted property
140                    throw new ArgumentException(
141                        string.Format(
142                            CultureInfo.CurrentCulture,
143                            "Unable to access DependencyProperty \"{0}\" on type \"{1}\".",
144                            propertyName,
145                            type.Name));
146                }
147  
148                BindingExpression binding = element1.GetBindingExpression(dp);
149                object value = element1.GetValue(dp);
150                if (binding != null)
151                {
152                    element2.SetBinding(dp, binding.ParentBinding);
153                }
154                else if (value != null)
155                {
156                    element2.SetValue(dp, value);
157                }
158                if (_mode == UpMode.Move)
159                    element1.ClearValue(dp);
160            }
161  
162            // Copyright (C) Microsoft Corporation. All Rights Reserved.
163            // This code released under the terms of the Microsoft Public License
164            // (Ms-PL, http://opensource.org/licenses/ms-pl.html).
165            /// <summary>
166            /// Gets a sequence of assemblies to search for the provided type name.
167            /// </summary>
168            private IEnumerable<Assembly> AssembliesToSearch
169            {
170                get
171                {
172                    // Start with the System.Windows assembly (home of all core controls)
173                    yield return typeof(Control).Assembly;
174  
175#if SILVERLIGHT && !WINDOWS_PHONE
176                    // Fall back by trying each of the assemblies in the Deployment's Parts list
177                    foreach (var part in Deployment.Current.Parts)
178                    {
179                        var streamResourceInfo = Application.GetResourceStream(
180                            new Uri(part.Source, UriKind.Relative));
181                        using (var stream = streamResourceInfo.Stream)
182                        {
183                            yield return part.Load(stream);
184                        }
185                    }
186#endif
187                }
188            }
189        }
190  
191        private enum UpMode
192        {
193            Move,
194            Copy,
195        }
196    }
197}

    如何使用?使用非常简单!

    在你的项目中增加 UpUp 之后,在需要提升绑定级别的 Page 的 Xaml 中引入命名空间 xmlns:delay="clr-namespace:Delay"。然后在需要提升绑定级别的控件中加入属性 delay:UpUp.Up="Grid.Row,Grid.Column"。得到完整的前台代码如下:

01<UserControl x:Class="TestValueBindingInItemTemplate.MainPage"
06    xmlns:delay="clr-namespace:Delay"
07    mc:Ignorable="d"
08    d:DesignHeight="300" d:DesignWidth="400">
09  
10    <Grid x:Name="LayoutRoot" Background="White">
11        <ItemsControl ItemsSource="{Binding}">
12            <ItemsControl.ItemTemplate>
13                <DataTemplate>
14                    <TextBox Text="{Binding Text}"
15                             Foreground="Green"
16                             Grid.Row="{Binding Row}"
17                             Grid.Column="{Binding Column}"
18                             Height="30" Width="150"
19                             delay:UpUp.Up="Grid.Row,Grid.Column"
20                             />
21                </DataTemplate>
22            </ItemsControl.ItemTemplate>
23            <ItemsControl.ItemsPanel>
24                <ItemsPanelTemplate>
25                    <Grid ShowGridLines="True">
26                        <Grid.RowDefinitions>
27                            <RowDefinition/>
28                            <RowDefinition/>
29                            <RowDefinition/>
30                        </Grid.RowDefinitions>
31                        <Grid.ColumnDefinitions>
32                            <ColumnDefinition/>
33                            <ColumnDefinition/>
34                        </Grid.ColumnDefinitions>
35                    </Grid>
36                </ItemsPanelTemplate>
37            </ItemsControl.ItemsPanel>
38        </ItemsControl>
39    </Grid>
40</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 部分放置到此方法内。

转载于:https://www.cnblogs.com/splendidme/archive/2011/09/14/2175385.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值