zoukankan      html  css  js  c++  java
  • 【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;
                else
                    return 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 name
                        type = assembly.GetTypes()
                           .Where(t => (t.FullName == typeName) || (t.Name == typeName))
                           .FirstOrDefault();
                        if (type != null)
                            break;
                    }
                    if (null == type)
                    {
                        // Unable to find the requested type anywhere
                        throw 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 Binding
                    DependencyProperty 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 property
                        throw 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 list
                        foreach (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 部分放置到此方法内。

    本文完整代码在此下载:https://files.cnblogs.com/Aimeast/SLTestValueBindingInItemTemplate.zip

  • 相关阅读:
    java利用zxing编码解码一维码与二维码
    Spring和MyBatis环境整合
    ML中Boosting和Bagging的比較
    理解x64代码模型
    python list.remove(),del()和filter &amp; lambda
    限制文本域中字符输入个数
    arcgis api for flex之专题图制作(饼状图,柱状图等)
    Linux I/O复用中select poll epoll模型的介绍及其优缺点的比較
    开发H5游戏引擎的选择:Egret或Laya?
    C++刷题——2830: 递归求1*1+2*2+3*3+……+n*n
  • 原文地址:https://www.cnblogs.com/Aimeast/p/2173788.html
Copyright © 2011-2022 走看看