潜移默化学会WPF--Command(命令)学习(五)(高级) - AYUI框架 - 博客园

原文: 潜移默化学会WPF--Command(命令)学习(五)(高级) - AYUI框架 - 博客园

5 突破底线,穷根究底

   5.1 本例用到了 依赖性属性 自定义控件 值转换器 还有一些C#委托和事件 继承相关的基础知识,让你更进一步了解Command,自己写一个拓展了 Slider 控件属性(Command相关的)的一个新的Slider控件

       先看前台代码

前台代码
<Window x:Class="Commands.CustomControlWithCommand"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Commands"
Title="CustomControlWithCommand" Height="500" Width="400"
>
<Window.Resources>
<local:FontStringValueConverter x:Key="StringConverterResource"/>
<local:FontDoubleValueConverter x:Key="DoubleConverterResource"/>
</Window.Resources>
<StackPanel>
<Border BorderBrush="Black"
BorderThickness="2"
Margin="10"
Width="400"
Height="400">
<StackPanel>
<StackPanel Margin="10">
<Label HorizontalAlignment="Center">
Custom Slider that Invokes a Command
</Label>
<Border Width="350" Background="LightBlue">
<local:CommandSlider x:Name="FontSlider"
Background="AliceBlue"
Minimum="0"
Maximum="60"
Value="12"
TickFrequency="5"
Height="50"
Width="275"
TickPlacement="BottomRight"
LargeChange="5"
SmallChange="5"
AutoToolTipPlacement="BottomRight"
AutoToolTipPrecision="0"
Command="{x:Static local:CustomControlWithCommand.FontUpdateCommand}"
CommandTarget="{Binding ElementName=txtBoxTarget}"
CommandParameter="{Binding ElementName=FontSlider,
Path=Value,
Converter={StaticResource DoubleConverterResource}}"
Focusable="False"/>

</Border>
</StackPanel>
<Border BorderBrush="Black"
BorderThickness="1"
Height="150"
Width="300"
Margin="15">
<StackPanel Margin="5">
<CheckBox IsChecked="False"
Checked="OnReadOnlyChecked"
Unchecked="OnReadOnlyUnChecked"
Content="Read Only"
Margin="5"
FontSize="12" />
<TextBox Name="txtBoxTarget" Height="100" Width="275" Margin="3">
<TextBox.CommandBindings>
<CommandBinding Command="{x:Static local:CustomControlWithCommand.FontUpdateCommand}"
Executed="SliderUpdateExecuted"
CanExecute="SliderUpdateCanExecute" />
</TextBox.CommandBindings>
Hello
</TextBox>
</StackPanel>
</Border>
<StackPanel>
<Label HorizontalAlignment="Center">
More Command Sources for the Font Update Command
</Label>
<StackPanel Margin="10" HorizontalAlignment="Left" Background="LightBlue">
<Button Command="{x:Static local:CustomControlWithCommand.FontUpdateCommand}"
CommandTarget="{Binding ElementName=txtBoxTarget}"
CommandParameter="{Binding ElementName=txtValue,
Path=Text,
Converter={
StaticResource StringConverterResource}}"
Height="50"
Width="150"
Margin="1">
Update Font Via Command
</Button>
<TextBox Name="txtValue"
MaxLength="2"
Width="25"
Background="AliceBlue"
Margin="0,0,0,3">5</TextBox>
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
</StackPanel>
</Window>

      后台代码

后台代码
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Globalization;

namespace Commands
{
/// <summary>
/// Interaction logic for CustomControlWithCommand.xaml
/// </summary>

public partial class CustomControlWithCommand : System.Windows.Window
{

public CustomControlWithCommand()
{
InitializeComponent();
}
public static RoutedCommand FontUpdateCommand = new RoutedCommand();

//The ExecutedRoutedEvent Handler
//if the command target is the TextBox, changes the fontsize to that
//of the value passed in through the Command Parameter
public void SliderUpdateExecuted(object sender, ExecutedRoutedEventArgs e)
{
TextBox source = sender as TextBox;

if (source != null)
{
if (e.Parameter != null)
{
try
{
if ((int)e.Parameter > 0 && (int)e.Parameter <= 60)
{
source.FontSize = (int)e.Parameter;
}
}
catch
{
MessageBox.Show("in Command \n Parameter: " + e.Parameter);
}

}
}
}

//The CanExecuteRoutedEvent Handler
//if the Command Source is a TextBox, then set CanExecute to ture;
//otherwise, set it to false
public void SliderUpdateCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
TextBox source = sender as TextBox;

if (source != null)
{
if (source.IsReadOnly)
{
e.CanExecute = false;
}
else
{
e.CanExecute = true;
}
}
}

//if the Readonly Box is checked, we need to force the CommandManager
//to raise the RequerySuggested event. This will cause the Command Source
//to query the command to see if it can execute or not.
public void OnReadOnlyChecked(object sender, RoutedEventArgs e)
{
if (txtBoxTarget != null)
{
txtBoxTarget.IsReadOnly = true;
CommandManager.InvalidateRequerySuggested();
}
}

//if the Readonly Box is checked, we need to force the CommandManager
//to raise the RequerySuggested event. This will cause the Command Source
//to query the command to see if it can execute or not.
public void OnReadOnlyUnChecked(object sender, RoutedEventArgs e)
{
if (txtBoxTarget != null)
{
txtBoxTarget.IsReadOnly = false;
CommandManager.InvalidateRequerySuggested();
}
}
}


//Converter to convert the Slider value property to an int
[ValueConversion(typeof(double), typeof(int))]
public class FontStringValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string fontSize = (string)value;
int intFont;

try
{
intFont = Int32.Parse(fontSize);
return intFont;
}
catch (FormatException e)
{
return null;
}
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}

//Converter to convert the Slider value property to an int
[ValueConversion(typeof(double), typeof(int))]
public class FontDoubleValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double fontSize = (double)value;
return (int)fontSize;
}

public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
}

      CommandSlider类

CommandSlider
复制代码

看了这么多代码,你可能要疯了,不过不要急,好多都是一个原理

5.2 潜移默化

    讲解:

         先讲CommandSlider类,首先他继承了   Slider 类,实现了 ICommandSource 接口,目的很明显了,就是拓展Slider控件,它本身就是一个Slider控件了,这点很重要! 那个接口只是为了能让调用者能调用Command相关的依赖性属性

       废话不多说,正题:

       导入命名控件 System.Windows.Input 和 System.Windows.Controls

       (快捷键  鼠标指在ICommand上  按Shift+Alt+F10 快速提示命名空间的一个快捷键)

    public CommandSlider()
            : base()
        { }

就是继承父类所有的东西,这个有关  C# OOP中的继承  在这里我就不多说了,没有学过 OOP 的还是尽量不要看了

       学过依赖性属性的都知道 依赖性属性怎么定义的了

       第一步:声明一个属性

例子:

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

 其中 CommandProperty是依赖性属性定义的规范,要在定义的属性名后面加Property,本例是CommandICommand类型的,所以后面加上Property就变成了CommandProperty;  这里的getset也特殊,也只能这样写GetValue等什么的,如果没注册 CommandProperty会出错,没关系放在那里,接下来注册

 

        第二步:注册依赖性属性

 public static readonly DependencyProperty CommandProperty =
            DependencyProperty.Register(
                "Command",
                typeof(ICommand),
                typeof(CommandSlider),
                new PropertyMetadata(

                                  (ICommand)null, new PropertyChangedCallback(CommandChanged))

                                   );

        这里的CommandProperty 就是那个对应的名字,Register(  那个属性名字它对应的类型本类的类名(一般都这样),其他处理),可以这样理解   PropertyChangedCallback  回调方法, CommandChanged 是一个方法的名称,看词义就知道是 Command的值改变时调用的,前面那个

(ICommand)null 是默认值,表示该Commandnull 


同理,注册 CommandTargetCommandParameter依赖性属性          备注:CommandTarget  这个命令所影响的对象  CommandParameter 是执行命令添加的一些参数

    

        //make CommandTarget a dependency property so it can be DataBound
        public static readonly DependencyProperty CommandTargetProperty =
            DependencyProperty.Register(
                "CommandTarget",
                typeof(IInputElement),
                typeof(CommandSlider),
                new PropertyMetadata((IInputElement)null));

        public IInputElement CommandTarget
        {
            get
            {
                return (IInputElement)GetValue(CommandTargetProperty);
            }
            set
            {
                SetValue(CommandTargetProperty, value);
            }
        }

        //make CommandParameter a dependency property so it can be DataBound
        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.Register(
                "CommandParameter",
                typeof(object),
                typeof(CommandSlider),
                new PropertyMetadata((object)null));

        public object CommandParameter
        {
            get
            {
                return (object)GetValue(CommandParameterProperty);
            }
            set
            {
                SetValue(CommandParameterProperty, value);
            }
        }




   第三步:其他步骤了,就按本例子去说吧

View Code
  // Command dependency property change callback
private static void CommandChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
CommandSlider cs = (CommandSlider)d;
cs.HookUpCommand((ICommand)e.OldValue, (ICommand)e.NewValue);
}
// Add a new command to the Command Property
private void HookUpCommand(ICommand oldCommand, ICommand newCommand)
{
//if oldCommand is not null, then we need to remove the handlers
if (oldCommand != null)
{
RemoveCommand(oldCommand, newCommand);
}
AddCommand(oldCommand, newCommand);
}

// Remove an old command from the Command Property
private void RemoveCommand(ICommand oldCommand, ICommand newCommand)
{
EventHandler handler = CanExecuteChanged;
oldCommand.CanExecuteChanged -= handler;
}

// add the command
private void AddCommand(ICommand oldCommand, ICommand newCommand)
{
EventHandler handler = new EventHandler(CanExecuteChanged);
canExecuteChangedHandler = handler;
if (newCommand != null)
{
newCommand.CanExecuteChanged += canExecuteChangedHandler;
}
}
private void CanExecuteChanged(object sender, EventArgs e)
{

if (this.Command != null)
{
RoutedCommand command = this.Command as RoutedCommand;

// if RoutedCommand
if (command != null)
{
if (command.CanExecute(CommandParameter, CommandTarget))
{
this.IsEnabled = true;
}
else
{
this.IsEnabled = false;
}
}
// if not RoutedCommand
else
{
if (Command.CanExecute(CommandParameter))
{
this.IsEnabled = true;
}
else
{
this.IsEnabled = false;
}
}
}
}



CommandChanged事件就是上面Command属性改变时触发的事件

 首先获得触发事件的事件源  这里肯定知道是CommandSlider了  所以CommandSlider cs = (CommandSlider)d; 例如 假如有N个 CommandSlider控件 就有NCommand属性,你不知道是哪个触发的,就可以这样干,这一个技巧非常经常用到,通过e.OldValue,e.NewValue获得改变前的值和改变后的值,你懂的,不说了

HookUpCommand  hookup是连接的意思,先不看   RemoveCommand(移除Command属性AddCommand(添加Command属性)  这两个方法

接下来我们来看CanExecuteChanged方法

因为Command属性是ICommand类型的,RoutedCommand实现了ICommand ,所以Command 可以转换为 RoutedCommand(路由命令)类型的,转换成功就可以处理了

如果是路由命令,通过command.CanExecute(2个参数)方法判断 此路由命令在当前状态下是否可行,可行就设为将控件状态设为可用(true),不可行设为不可用

如果不是路由命令,通过command.CanExecute(1个参数)方法判断 此路由命令在当前状态下是否可行,可行就设为将控件状态设为可用(true),不可行设为不可用

接下来讲解:

 RemoveCommand(移除Command属性) 和AddCommand(添加Command属性)  这两个方法,还有HookUpCommand方法

思路:改变值是有2个值,一个是原本的值,一个是新值 ,原值不为空,就先移除原值上的CanExecuteChanged事件,这样的话当老值改变的时候就不会调用他的CanExecuteChanged事件了,就不会影响到新值了。

接下来就可以在  新值上绑定 CanExecuteChanged 事件,

插一句     EventHandler handler = new EventHandler(CanExecuteChanged);   等同于    EventHandler handler = CanExecuteChanged;  学委托和事件的时候应该都知道,这是基本的  ,这里是在EventHandler上绑定一个CanExecuteChanged事件

为了避免GC(垃圾收集器)回收了,我们创建一个副本   private static EventHandler canExecuteChangedHandler;

canExecuteChangedHandler = handler;
if (newCommand != null)
{
newCommand.CanExecuteChanged += canExecuteChangedHandler;
}

在新值上绑定个CanExecuteChanged事件;

还有一个重写父类的OnValueChanged的方法,就是在原来的Slider控件的这个事件中拓展了一下,这个方法干嘛的呢?

就是如果 Command是定义好的,那么移动滑条的时候,这个控件(Slider,现在是CommandSlider)就会调用(invoke)这个Command,否则的话这个滑条就是个很普通的滑条,因为看他的参数就知道他没有绑定 受影响的对象。发现了规律:RoutedCommand(路由命令)的Execute有两个参数,包含了CommandTarget,而ICommand的Executed没有CommandTarget,这样区分了,下面我来俗语话讲解 路由命令 来理解 路由 这个抽象概念  就是为什么路由命令才能有效果。


路由命令:   假设  A 是 Slider控件     B代表TextBox控件   我现在想通过Slider的滑条改变TextBox中的字体大小的值,  有很多方式实现,这里用命令实现   现在把       ”字体变大“ 这个信号叫做要路由的东西C。 路由是父类,命令路由是子类,因为路由有很多种,比如事件路由等

很明显,现在主要母的是要通过 滑条 控制 文本框 的变化,2个对象,可以这样读,A把C路由到了B上  在具体一点   A把C通过 “命令路由”这类路由  影响到了B上,然后B受到影响,字体变大了,现在懂了吧,我不知道对不对,专业的请谅解一下,呵呵,谢谢了 


到此为止应该完成了CommandSlider的讲解了,主要目的 是 我们手动给一个没有Command相关的依赖性属性的控件 添加Command相关的依赖性属性,到此为止,我们的Slider升级版的CommandSlider已经完成了,接下来就是用了


在后台代码中...

先讲一下值转换器

  就是一个继承了  IValueConverter接口的类,实现他们 主要实现 Convert方法 另一个方法可以保持默认,这里有2个值转换器,一个是将string转换成int

  还有1个是将double转换成int的

  转换器写好了,前台就可以  在绑定时将path后面的值作为条件转换成另一种类型的值了,例如:

{Binding ElementName=FontSlider,
Path=Value,
Converter={StaticResource DoubleConverterResource}

 

值转换器这里 我就不说了,以后我还会写文章介绍的

 

 

老朋友了,后台 定义2个 CanExecute的方法和Executed方法就行了,这里可以参考前面4章理解 就知道我为什么要定义这2个方法了;

 

前台代码

复制代码
  <local:CommandSlider x:Name="FontSlider"
Background="AliceBlue"
Minimum="0"
Maximum="60"
Value="12"
TickFrequency="5"
Height="50"
Width="275"
TickPlacement="BottomRight"
LargeChange="5"
SmallChange="5"
AutoToolTipPlacement="BottomRight"
AutoToolTipPrecision="0"
Command="{x:Static local:CustomControlWithCommand.FontUpdateCommand}"
CommandTarget="{Binding ElementName=txtBoxTarget}"
CommandParameter="{Binding ElementName=FontSlider,
Path=Value,
Converter={StaticResource DoubleConverterResource}}"
Focusable="False"/>
复制代码


很清楚的就看到了Command CommandTarget CommandParameter这3个依赖性属性了  对应了   路由命令类型的命令   路由目标  下面是一个参数 后面还有一个Converter就是转换器了

 

 

     <TextBox Name="txtBoxTarget" Height="100" Width="275" Margin="3">
<TextBox.CommandBindings>
<CommandBinding Command="{x:Static local:CustomControlWithCommand.FontUpdateCommand}"
Executed="SliderUpdateExecuted"
CanExecute="SliderUpdateCanExecute" />
</TextBox.CommandBindings>

文本框调用方法

 这是一篇关于   CommandManager.InvalidateRequerySuggested()   的讲解的链接,我看了还不错,不理解的可以看一下

http://www.cnblogs.com/mgen/archive/2011/05/29/2062170.html

 

复制代码

 

分类: WPF
1
0
« 上一篇: 潜移默化学会WPF--Command(命令)学习(四)
» 下一篇: 这是一个metro 风格的图标库,你可以查到每个图标,你可以把这个矢量图标转换成xaml代码,是Path类型的,用在WPF,SL上
posted on 2019-03-01 22:13 NET未来之路 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/lonelyxmas/p/10459062.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值