【Silverlight】解決DataTemplate綁定附加屬性

來源:互聯網
上載者:User

    本文 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 部分放置到此方法內。

 

本文完整代碼在此下載:http://files.cnblogs.com/Aimeast/SLTestValueBindingInItemTemplate.zip

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.