WPF在不一樣.Net版本間的兼容性問題

第一次寫博客^_^app

以前在作.NET和WPF開發,期間遇到了一些.net不一樣版本之間兼容性問題,整理了下,留個備份。ide

.net 3.5的兼容性問題this

1. 在.net 3.5中VirtualizingStackPanel.VirtualizationMode沒有Recycling模式(是在.net 4.0後),因此用Standard替代。spa

    https://connect.microsoft.com/VisualStudio/feedback/details/346158/virtualizingstackpanel-virtualizationmode-not-working-properly.net

2. Command & CommandParameter in InputBindings are Properties not Dependency Properties,因此不能在xaml中直接用binding,解決方案本身從新實現Command和CommandParameter的類,裏面定義Dependence Properties,再在xaml把相應的Command和CommanParameter定義成StaticResource reference。code

New Command class:orm

using System;
using System.Windows;
using System.Windows.Input;

namespace Sample.WPFUI.Commands
{
    public class CommandReference : Freezable, ICommand
    {
        public CommandReference()
        {
        }

        public static readonly DependencyProperty CommandProperty = DependencyProperty.Register("Command", typeof(ICommand), typeof(CommandReference),
                                                                                                new PropertyMetadata(new PropertyChangedCallback(OnCommandChanged)));

        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        #region ICommand Members

        public bool CanExecute(object parameter)
        {
            if (Command != null)
                return Command.CanExecute(parameter);
            return false;
        }

        public void Execute(object parameter)
        {
            Command.Execute(parameter);
        }

        public event EventHandler CanExecuteChanged;

        private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            CommandReference commandReference = d as CommandReference;
            ICommand oldCommand = e.OldValue as ICommand;
            ICommand newCommand = e.NewValue as ICommand;

            if (oldCommand != null)
            {
                oldCommand.CanExecuteChanged -= commandReference.CanExecuteChanged;
            }
            if (newCommand != null)
            {
                newCommand.CanExecuteChanged += commandReference.CanExecuteChanged;
            }
        }

        #endregion

        #region Freezable

        protected override Freezable CreateInstanceCore()
        {
            throw new NotImplementedException();
        }

        #endregion
    }
}
View Code

New CommandParameter class:blog

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using System.Windows;
using System.Windows.Markup;

namespace Sample.WPFUI
{
    public class DataResource : Freezable
    {
        /// <summary>
        /// Identifies the <see cref="BindingTarget"/> dependency property.
        /// </summary>
        /// <value>
        /// The identifier for the <see cref="BindingTarget"/> dependency property.
        /// </value>
        public static readonly DependencyProperty BindingTargetProperty = DependencyProperty.Register("BindingTarget", typeof(object), typeof(DataResource), new UIPropertyMetadata(null));

        /// <summary>
        /// Initializes a new instance of the <see cref="DataResource"/> class.
        /// </summary>
        public DataResource()
        {
        }

        /// <summary>
        /// Gets or sets the binding target.
        /// </summary>
        /// <value>The binding target.</value>
        public object BindingTarget
        {
            get { return (object)GetValue(BindingTargetProperty); }
            set { SetValue(BindingTargetProperty, value); }
        }

        /// <summary>
        /// Creates an instance of the specified type using that type's default constructor. 
        /// </summary>
        /// <returns>
        /// A reference to the newly created object.
        /// </returns>
        protected override Freezable CreateInstanceCore()
        {
            return (Freezable)Activator.CreateInstance(GetType());
        }

        /// <summary>
        /// Makes the instance a clone (deep copy) of the specified <see cref="Freezable"/>
        /// using base (non-animated) property values. 
        /// </summary>
        /// <param name="sourceFreezable">
        /// The object to clone.
        /// </param>
        protected sealed override void CloneCore(Freezable sourceFreezable)
        {
            base.CloneCore(sourceFreezable);
        }
    }

    public class DataResourceBindingExtension : MarkupExtension
    {
        private object mTargetObject;
        private object mTargetProperty;
        private DataResource mDataResouce;

        /// <summary>
        /// Gets or sets the data resource.
        /// </summary>
        /// <value>The data resource.</value>
        public DataResource DataResource
        {
            get
            {
                return mDataResouce;
            }
            set
            {
                if (mDataResouce != value)
                {
                    if (mDataResouce != null)
                    {
                        mDataResouce.Changed -= DataResource_Changed;
                    }
                    mDataResouce = value;

                    if (mDataResouce != null)
                    {
                        mDataResouce.Changed += DataResource_Changed;
                    }
                }
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="DataResourceBindingExtension"/> class.
        /// </summary>
        public DataResourceBindingExtension()
        {
        }

        /// <summary>
        /// When implemented in a derived class, returns an object that is set as the value of the target property for this markup extension.
        /// </summary>
        /// <param name="serviceProvider">Object that can provide services for the markup extension.</param>
        /// <returns>
        /// The object value to set on the property where the extension is applied.
        /// </returns>
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            IProvideValueTarget target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));

            mTargetObject = target.TargetObject;
            mTargetProperty = target.TargetProperty;

            // mTargetProperty can be null when this is called in the Designer.
            Debug.Assert(mTargetProperty != null || DesignerProperties.GetIsInDesignMode(new DependencyObject()));

            if (DataResource.BindingTarget == null && mTargetProperty != null)
            {
                PropertyInfo propInfo = mTargetProperty as PropertyInfo;
                if (propInfo != null)
                {
                    try
                    {
                        return Activator.CreateInstance(propInfo.PropertyType);
                    }
                    catch (MissingMethodException)
                    {
                        // there isn't a default constructor
                    }
                }

                DependencyProperty depProp = mTargetProperty as DependencyProperty;
                if (depProp != null)
                {
                    DependencyObject depObj = (DependencyObject)mTargetObject;
                    return depObj.GetValue(depProp);
                }
            }

            return DataResource.BindingTarget;
        }

        private void DataResource_Changed(object sender, EventArgs e)
        {
            // Ensure that the bound object is updated when DataResource changes.
            DataResource dataResource = (DataResource)sender;
            DependencyProperty depProp = mTargetProperty as DependencyProperty;

            if (depProp != null)
            {
                DependencyObject depObj = (DependencyObject)mTargetObject;
                object value = Convert(dataResource.BindingTarget, depProp.PropertyType);
                depObj.SetValue(depProp, value);
            }
            else
            {
                PropertyInfo propInfo = mTargetProperty as PropertyInfo;
                if (propInfo != null)
                {
                    object value = Convert(dataResource.BindingTarget, propInfo.PropertyType);
                    propInfo.SetValue(mTargetObject, value, new object[0]);
                }
            }
        }

        private object Convert(object obj, Type toType)
        {
            try
            {
                return System.Convert.ChangeType(obj, toType);
            }
            catch (InvalidCastException)
            {
                return obj;
            }
        }
    }
}
View Code

 Xaml sample:ci

<cf:CommandReference x:Key="EditTextBoxReturnCommand" Command="{Binding Path=DataContext.ViewModelCommands.EditTextBoxReturnCommand, ElementName=filePaneCloudViewPage}"/> 
<cf:CommandReference x:Key="EditTextBoxEscapeCommand" Command="{Binding Path=DataContext.ViewModelCommands.EditTextBoxEscapeCommand, ElementName=filePaneCloudViewPage}"/> 

<TextBox.Resources>
    <wpf:DataResource x:Key="EditTextBoxReturnCommandParameter" BindingTarget="{Binding Path=Content, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}}}"/> 
    <wpf:DataResource x:Key="EditTextBoxEscapeCommandParameter" BindingTarget="{Binding ElementName=editFileNameTextBox}"/> 
</TextBox.Resources> 

<TextBox.InputBindings> 
    <KeyBinding Key="Return" CommandParameter="{StaticResource EditTextBoxReturnCommandParameter}" Command="{StaticResource EditTextBoxReturnCommand}"/> 
    <KeyBinding Key="Escape" CommandParameter="{StaticResource EditTextBoxEscapeCommandParameter}" Command="{StaticResource EditTextBoxEscapeCommand}"/>
</TextBox.InputBindings>

 

.net 4.0的兼容性問題開發

1. 不支持在Xaml有嵌套的template

<ListBox ItemsSource="{Binding Mylist}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <ListBox ItemsSource="{Binding}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding}" Loaded="TextBlock_Loaded" Style="{StaticResource TextBlockStyle}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
            </ListBox>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

解決方案

1. Add the datatemplate as a StaticResource reference

<ListBox ItemsSource="{Binding Mylist}"> 
    <ListBox.ItemTemplate> 
        <DataTemplate> 
            <DataTemplate.Resources> 
                <DataTemplate x:Key="nestedDataTemplate"> 
                    <TextBlock Text="{Binding}" Loaded="TextBlock_Loaded" Style="{DynamicResource TextBlockStyle}" /> 
                </DataTemplate> 
            </DataTemplate.Resources> 
            <ListBox ItemsSource="{Binding}" ItemTemplate="{StaticResource nestedDataTemplate}"/> 
        </DataTemplate> 
    </ListBox.ItemTemplate> 
</ListBox> 

2. Use DynamicResource for the style

<TextBlock Text="{Binding}" Loaded="TextBlock_Loaded" Style="{DynamicResource TextBlockStyle}" />
相關文章
相關標籤/搜索