zoukankan      html  css  js  c++  java
  • 一种用XAML写Data Converter的方式

    在WPF程序中,数据绑定是非常常用的手段。伴随着数据绑定,我们通常还需要编写一些Converter。而编写Converter是一件非常枯燥的事情,并且大量的converter不容易组织和维护。

    今天在网上发现了一篇文章SwitchConverter – A "switch statement" for XAML,它可以通过XAML的方式编写一些类似switch-case方式的converter,十分简洁明了。例如,对如如下的数据绑定转换:

        

    可以直接在XAML中通过如下方式写converter:

    <Grid>
        <Grid.Resources>
            <e:SwitchConverter x:Key="WeatherIcons">
                <e:SwitchCase When="Sunny" Then="Sunny.png" />
                <e:SwitchCase When="Cloudy" Then="Cloudy.png" />
                <e:SwitchCase When="Rain" Then="Rain.png" />
                <e:SwitchCase When="Snow" Then="Snow.png" />
            </e:SwitchConverter>
        </Grid.Resources>
        <Image Source="{Binding Condition, Converter={StaticResource WeatherIcons}}" />
    </Grid>

    原文已经附上了代码的工程,但由于担心哪天方校长抖威风而导致该文章失效,这里将其转录了下来,一共三个文件:

    SwitchCase.cs

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Diagnostics.Contracts;
    using System.Linq;
    using System.Windows;
    using System.Windows.Markup;
    
    namespace SwitchConverterDemo
    {
    
        /// <summary>
        /// An individual case in the switch statement.
        /// </summary>
        [ContentProperty( "Then" )]
        public sealed class SwitchCase : DependencyObject
        {
    
            #region Constructors
    
            /// <summary>
            /// Initializes a new instance of the <see cref="T:SwitchCase"/> class.
            /// </summary>
            public SwitchCase( )
            {
    
            }
    
            #endregion
    
            #region Properties
    
            /// <summary>
            /// Dependency property for the <see cref="P:When"/> property.
            /// </summary>
            public static readonly DependencyProperty WhenProperty = DependencyProperty.Register( "When", typeof( object ), typeof( SwitchCase ), new PropertyMetadata( default( object ) ) );
    
            /// <summary>
            /// The value to match against the input value.
            /// </summary>
            public object When
            {
                get
                {
                    return (object)GetValue( WhenProperty );
                }
                set
                {
                    SetValue( WhenProperty, value );
                }
            }
    
            /// <summary>
            /// Dependency property for the <see cref="P:Then"/> property.
            /// </summary>
            public static readonly DependencyProperty ThenProperty = DependencyProperty.Register( "Then", typeof( object ), typeof( SwitchCase ), new PropertyMetadata( default( object ) ) );
    
            /// <summary>
            /// The output value to use if the current case matches.
            /// </summary>
            public object Then
            {
                get
                {
                    return (object)GetValue( ThenProperty );
                }
                set
                {
                    SetValue( ThenProperty, value );
                }
            }
    
            #endregion
    
        }   // class
    
    }   // namespace
    View Code

    SwitchCaseCollection.cs

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Diagnostics.Contracts;
    using System.Linq;
    
    namespace SwitchConverterDemo
    {
    
        /// <summary>
        /// A collection of switch cases.
        /// </summary>
        public sealed class SwitchCaseCollection : Collection<SwitchCase>
        {
    
            #region Constructors
    
            /// <summary>
            /// Initializes a new instance of the <see cref="T:SwitchCaseCollection"/> class.
            /// </summary>
            internal SwitchCaseCollection( )
            {
    
            }
    
            #endregion
    
            #region Methods
    
            /// <summary>
            /// Adds a new case to the collection.
            /// </summary>
            /// <param name="when">The value to compare against the input.</param>
            /// <param name="then">The output value to use if the case matches.</param>
            public void Add( object when, object then )
            {
                Add(
                    new SwitchCase {
                        When = when,
                        Then = then
                    }
                );
            }
    
            #endregion
    
        }   // class
    
    }   // namespace
    View Code

    SwitchConverter.cs

    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.ComponentModel;
    using System.Diagnostics.Contracts;
    using System.Globalization;
    using System.Linq;
    using System.Windows;
    using System.Windows.Data;
    using System.Windows.Markup;
    
    namespace SwitchConverterDemo
    {
    
        /// <summary>
        /// Produces an output value based upon a collection of case statements.
        /// </summary>
        [ContentProperty( "Cases" )]
        public class SwitchConverter : IValueConverter
        {
    
            #region Constructors
    
            /// <summary>
            /// Initializes a new instance of the <see cref="T:SwitchConverter"/> class.
            /// </summary>
            public SwitchConverter( )
                : this( new SwitchCaseCollection( ) )
            {
            }
    
            /// <summary>
            /// Initializes a new instance of the <see cref="T:SwitchConverter"/> class.
            /// </summary>
            /// <param name="cases">The case collection.</param>
            internal SwitchConverter( SwitchCaseCollection cases )
            {
    
                Contract.Requires( cases != null );
    
                Cases = cases;
                StringComparison = StringComparison.OrdinalIgnoreCase;
    
            }
    
            #endregion
    
            #region Properties
    
            /// <summary>
            /// Holds a collection of switch cases that determine which output
            /// value will be produced for a given input value.
            /// </summary>
            public SwitchCaseCollection Cases
            {
                get;
                private set;
            }
    
            /// <summary>
            /// Specifies the type of comparison performed when comparing the input
            /// value against a case.
            /// </summary>
            public StringComparison StringComparison
            {
                get;
                set;
            }
    
            /// <summary>
            /// An optional value that will be output if none of the cases match.
            /// </summary>
            public object Else
            {
                get;
                set;
            }
    
            #endregion
    
            #region Methods
    
            /// <summary>
            /// Converts a value.
            /// </summary>
            /// <param name="value">The value produced by the binding source.</param>
            /// <param name="targetType">The type of the binding target property.</param>
            /// <param name="parameter">The converter parameter to use.</param>
            /// <param name="culture">The culture to use in the converter.</param>
            /// <returns>A converted value. If the method returns null, the valid null value is used.</returns>
            public object Convert( object value, Type targetType, object parameter, CultureInfo culture )
            {
    
                if ( value == null ) {
                    
                    // Special case for null
                    // Null input can only equal null, no convert necessary
                    
                    return Cases.FirstOrDefault( x => x.When == null ) ?? Else;
    
                }
    
                foreach ( var c in Cases.Where( x => x.When != null ) ) {
    
                    // Special case for string to string comparison
                    if ( value is string && c.When is string ) {
                        if ( String.Equals( (string)value, (string)c.When, StringComparison ) ) {
                            return c.Then;
                        }
                    }
    
                    object when = c.When;
    
                    // Normalize the types using IConvertible if possible
                    if ( TryConvert( culture, value, ref when ) ) {
                        if ( value.Equals( when ) ) {
                            return c.Then;
                        }
                    }
    
                }
    
                return Else;
    
            }
    
            /// <summary>
            /// Converts a value.
            /// </summary>
            /// <param name="value">The value that is produced by the binding target.</param>
            /// <param name="targetType">The type to convert to.</param>
            /// <param name="parameter">The converter parameter to use.</param>
            /// <param name="culture">The culture to use in the converter.</param>
            /// <returns>A converted value. If the method returns null, the valid null value is used.</returns>
            public object ConvertBack( object value, Type targetType, object parameter, CultureInfo culture )
            {
                throw new NotSupportedException( );
            }
    
            /// <summary>
            /// Attempts to use the IConvertible interface to convert <paramref name="value2"/> into a type
            /// compatible with <paramref name="value1"/>.
            /// </summary>
            /// <param name="culture">The culture.</param>
            /// <param name="value1">The input value.</param>
            /// <param name="value2">The case value.</param>
            /// <returns>True if conversion was performed, otherwise false.</returns>
            private static bool TryConvert( CultureInfo culture, object value1, ref object value2 )
            {
    
                Type type1 = value1.GetType( );
                Type type2 = value2.GetType( );
    
                if ( type1 == type2 ) {
                    return true;
                }
    
                if ( type1.IsEnum ) {
                    value2 = Enum.Parse( type1, value2.ToString( ), true );
                    return true;
                }
    
                var convertible1 = value1 as IConvertible;
                var convertible2 = value2 as IConvertible;
    
                if ( convertible1 != null && convertible2 != null ) {
                    value2 = System.Convert.ChangeType( value2, type1, culture );
                    return true;
                }
    
                return false;
    
            }
    
            #endregion
    
        }   // class
    
    }   // namespace
    View Code

    这种绑定的方式非常简洁有效,但也有限制,只能处理简单的switch-case形式的关联,并且不能有转换逻辑。不过已经可以替换很大一部分Converter了(非常典型的应用就是这种枚举到图片的转换)。

    另外,网上也有一些开源库,实现了一些常见的通用Converter。例如:http://wpfconverters.codeplex.com/。在自己编写Converter之前,不妨先使用这些通用的Converter。

  • 相关阅读:
    VS 快捷键
    vue 本地环境API代理设置和解决跨域
    vue-cli 项目配置
    stylus 使用小技巧(1)
    vue 初始化rem
    vue element-ui NavMenu错位问题
    vue 数字输入组件
    vue X-Template
    vue 异步组件
    vue 非父子组件通信
  • 原文地址:https://www.cnblogs.com/TianFang/p/3238996.html
Copyright © 2011-2022 走看看