posts - 564, comments - 10438, trackbacks - 594, articles - 0
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

公告

背水一战 Windows 10 (67) - 控件(控件基类): DependencyObject - CoreDispatcher, 依赖属性的设置与获取, 依赖属性的变化回调

Posted on 2017-10-09 08:47 webabcd 阅读(...) 评论(...) 编辑 收藏

[源码下载]


背水一战 Windows 10 (67) - 控件(控件基类): DependencyObject - CoreDispatcher, 依赖属性的设置与获取, 依赖属性的变化回调



作者:webabcd


介绍
背水一战 Windows 10 之 控件(控件基类 - DependencyObject )

  • CoreDispatcher
  • 依赖属性的设置与获取
  • 依赖属性的变化回调



示例
1、演示 CoreDispatcher 的应用
Controls/BaseControl/DependencyObjectDemo/CoreDispatcherDemo.xaml

<Page
    x:Class="Windows10.Controls.BaseControl.DependencyObjectDemo.CoreDispatcherDemo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Windows10.Controls.BaseControl.DependencyObjectDemo"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="10 0 10 10">

            <TextBlock Name="lblMsg1" Margin="5" />

            <TextBlock Name="lblMsg2" Margin="5" />

            <TextBlock Name="lblMsg3" Margin="5" />

        </StackPanel>
    </Grid>
</Page>

Controls/BaseControl/DependencyObjectDemo/CoreDispatcherDemo.xaml.cs

/*
 * DependencyObject - 依赖对象(可以在 DependencyObject 上定义 DependencyProperty)
 *     Dispatcher - 获取 CoreDispatcher 对象
 *     
 *     
 * CoreDispatcher - 核心调度器
 *     CurrentPriority - 获取或设置当前任务的优先级(CoreDispatcherPriority 枚举)
 *     HasThreadAccess - 获取一个值,用于说明当前线程是否可更新此 CoreDispatcher 线程上的 UI
 *     RunAsync(), RunIdleAsync(), TryRunAsync(), TryRunIdleAsync() - 在此 CoreDispatcher 线程上使用指定的优先级执行指定的方法(一般用于更新 CoreDispatcher 线程上的 UI)
 *     ShouldYield() - 用于获知当前任务队列中是否存在更高优先级的任务,或指定的优先级及其以上的任务
 *     AcceleratorKeyActivated - 有按键操作时触发的事件(针对 UIElement 的键盘事件监听请参见:/Controls/BaseControl/UIElementDemo/KeyDemo.xaml)
 *     
 * CoreDispatcherPriority - 任务优先级枚举(High, Normal, Low, Idle)
 * 
 * 在 UWP 中优先级从高到低的排序如下
 * 1、本地代码中的 SendMessage
 * 2、CoreDispatcherPriority.High
 * 3、CoreDispatcherPriority.Normal
 * 4、所有设备输入消息
 * 5、CoreDispatcherPriority.Low
 * 6、CoreDispatcherPriority.Idle(一般用于后台任务)
 * 
 * 
 * 本例用于演示 DependencyObject 的 Dispatcher(CoreDispatcher 类型) 的应用
 */

using System;
using System.Threading;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace Windows10.Controls.BaseControl.DependencyObjectDemo
{
    public sealed partial class CoreDispatcherDemo : Page
    {
        public CoreDispatcherDemo()
        {
            this.InitializeComponent();

            this.Loaded += CoreDispatcherDemo_Loaded;

            // 监听按键事件
            Window.Current.CoreWindow.Dispatcher.AcceleratorKeyActivated += Dispatcher_AcceleratorKeyActivated;
        }

        private void CoreDispatcherDemo_Loaded(object sender, RoutedEventArgs e)
        {
            Timer timer =  new Timer((p) => 
            {
                // 当前线程是否可修改 CoreDispatcher 上的 UI
                if (base.Dispatcher.HasThreadAccess)
                {
                    lblMsg1.Text = "相同线程 " + DateTime.Now.ToString("mm:ss");
                }
                else
                {
                    // 非 UI 线程通过 CoreDispatcher 更新 UI
                    var ignored = base.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                    {
                        lblMsg1.Text = "不同线程 " + DateTime.Now.ToString("mm:ss");
                    });
                }
            }, null, TimeSpan.Zero, TimeSpan.FromSeconds(1));


            DispatcherTimer dTimer = new DispatcherTimer();
            dTimer.Interval = TimeSpan.FromSeconds(1);
            dTimer.Tick += (x, y) => 
            {
                // 当前线程是否可修改 Dispatcher 上的 UI
                if (base.Dispatcher.HasThreadAccess)
                {
                    lblMsg2.Text = "相同线程 " + DateTime.Now.ToString("mm:ss");
                }
                else
                {
                    // 非 UI 线程通过 CoreDispatcher 更新 UI
                    var ignored = base.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                    {
                        lblMsg2.Text = "不同线程 " + DateTime.Now.ToString("mm:ss");
                    });
                }
            };
            dTimer.Start();
        }

        private void Dispatcher_AcceleratorKeyActivated(CoreDispatcher sender, AcceleratorKeyEventArgs args)
        {
            /*
             * AcceleratorKeyEventArgs - 按键事件参数
             *     EventType - 事件类型(比如 KeyDown, KeyUp 之类的,详细参见 CoreAcceleratorKeyEventType 枚举)
             *     VirtualKey - 按键枚举(比如 A, B, C, D, LeftControl 之类的,详细参见 VirtualKey 枚举)
             *     KeyStatus - 按键的状态(一个 CorePhysicalKeyStatus 类型的对象,有好多字段,详细参见文档)
             */

            lblMsg3.Text += $"EventType:{args.EventType}, VirtualKey:{args.VirtualKey}, IsExtendedKey:{args.KeyStatus.IsExtendedKey}, IsKeyReleased:{args.KeyStatus.IsKeyReleased}, IsMenuKeyDown:{args.KeyStatus.IsMenuKeyDown}, RepeatCount:{args.KeyStatus.RepeatCount}, ScanCode:{args.KeyStatus.ScanCode}, WasKeyDown:{args.KeyStatus.WasKeyDown}";
            lblMsg3.Text += Environment.NewLine;

            // 屏蔽系统对按键的处理
            args.Handled = true;
        }
    }
}


2、演示依赖属性的设置与获取
Controls/BaseControl/DependencyObjectDemo/DependencyPropertyDemo.xaml

    <Page
    x:Class="Windows10.Controls.BaseControl.DependencyObjectDemo.DependencyPropertyDemo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Windows10.Controls.BaseControl.DependencyObjectDemo"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <StackPanel Margin="10 0 10 10">

            <Rectangle Name="rect1" Height="50" Fill="Red" HorizontalAlignment="Left" Margin="5" />

            <Rectangle Name="rect2" Height="50" Fill="Red" HorizontalAlignment="Left" Margin="5" />

            <Rectangle Name="rect3" Height="50" Fill="Red" HorizontalAlignment="Left" Margin="5" />

            <Rectangle Name="rect4" Width="400" Height="50" Fill="Red" Margin="5" />

            <Button Name="button" Content="点我" Click="button_Click" Margin="5" />

            <TextBlock Name="lblMsg" Margin="5" />

        </StackPanel>
    </Grid>
</Page>

Controls/BaseControl/DependencyObjectDemo/DependencyPropertyDemo.xaml.cs

/*
 * DependencyObject - 依赖对象(可以在 DependencyObject 上定义 DependencyProperty)
 *     SetValue(DependencyProperty dp, object value) - 设置依赖属性的值
 *     ClearValue(DependencyProperty dp) - 清除依赖属性的值
 *     GetValue(DependencyProperty dp) - 获取依赖属性的当前值
 *     GetAnimationBaseValue(DependencyProperty dp) - 获取依赖属性的基值
 *     ReadLocalValue(DependencyProperty dp) - 获取依赖属性的本地值
 *     
 *     
 * 本例用于演示 DependencyObject 的依赖属性的设置与获取
 */

using System;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
using Windows.UI.Xaml.Shapes;

namespace Windows10.Controls.BaseControl.DependencyObjectDemo
{
    public sealed partial class DependencyPropertyDemo : Page
    {
        public DependencyPropertyDemo()
        {
            this.InitializeComponent();

            this.Loaded += DependencyPropertyDemo_Loaded;
        }

        private void DependencyPropertyDemo_Loaded(object sender, RoutedEventArgs e)
        {
            // 直接设置依赖属性(rect1.Width = 400; 其实调用的就是这个)
            rect1.SetValue(FrameworkElement.WidthProperty, 400);


            // 直接设置依赖属性
            rect2.SetValue(FrameworkElement.WidthProperty, 200);
            // 通过动画的方式修改依赖属性的值
            SetRectWidth(rect2);


            // 通过 Style 的方式设置依赖属性
            Style style = new Style();
            style.TargetType = typeof(Rectangle);
            Setter setter = new Setter();
            setter.Property = Rectangle.WidthProperty;
            setter.Value = 200;
            style.Setters.Add(setter);
            rect3.Style = style;
            // 通过动画的方式修改依赖属性的值
            SetRectWidth(rect3);


            // 清除依赖属性的值
            rect4.ClearValue(FrameworkElement.WidthProperty);
        }
        
        private void button_Click(object sender, RoutedEventArgs e)
        {
            DependencyProperty dp = FrameworkElement.WidthProperty;

            // 通过 SetValue 设置依赖属性后,再通过 GetValue, GetAnimationBaseValue, ReadLocalValue 取出的值和设置的值都是一样的
            lblMsg.Text = $"rect1 GetValue:{rect1.GetValue(dp)}, GetAnimationBaseValue:{rect1.GetAnimationBaseValue(dp)}, ReadLocalValue:{rect1.ReadLocalValue(dp)}";
            lblMsg.Text += Environment.NewLine;

            // 通过 SetValue 设置依赖属性后,再通过动画的方式修改其值
            // GetValue - 获取当前值
            // GetAnimationBaseValue - 获取基值,也就是动画之前的值
            // ReadLocalValue - 获取本地值,即通过 SetValue 或者资源或者绑定设置的值
            lblMsg.Text += $"rect2 GetValue:{rect2.GetValue(dp)}, GetAnimationBaseValue:{rect2.GetAnimationBaseValue(dp)}, ReadLocalValue:{rect2.ReadLocalValue(dp)}";
            lblMsg.Text += Environment.NewLine;

            // 通过 Style 设置依赖属性后,再通过动画的方式修改其值
            // GetValue - 获取当前值
            // GetAnimationBaseValue - 获取基值,也就是动画之前的值
            // ReadLocalValue - 获取本地值,即通过 SetValue 或者资源或者绑定设置的值(如果是通过其他方式,比如 Style 方式设置的值,则无本地值)
            lblMsg.Text += $"rect3 GetValue:{rect3.GetValue(dp)}, GetAnimationBaseValue:{rect3.GetAnimationBaseValue(dp)}, ReadLocalValue:{rect3.ReadLocalValue(dp)}";
            lblMsg.Text += Environment.NewLine;

            // 通过 ClearValue 清除了依赖属性的值,则再通过 GetValue, GetAnimationBaseValue, ReadLocalValue 均无法获取到值
            lblMsg.Text += $"rect4 GetValue:{rect4.GetValue(dp)}, GetAnimationBaseValue:{rect4.GetAnimationBaseValue(dp)}, ReadLocalValue:{rect4.ReadLocalValue(dp)}, ActualWidth:{rect4.ActualWidth}";
        }


        private void SetRectWidth(Rectangle rect)
        {
            DoubleAnimation da = new DoubleAnimation();
            da.EnableDependentAnimation = true;
            da.BeginTime = TimeSpan.Zero;
            da.To = 400;
            da.Duration = TimeSpan.FromSeconds(5);
            Storyboard.SetTarget(da, rect);
            Storyboard.SetTargetProperty(da, nameof(rect.Width));

            Storyboard sb = new Storyboard();
            sb.Children.Add(da);
            sb.Begin();
        }
    }
}


3、演示如何监听依赖属性的变化
Controls/BaseControl/DependencyObjectDemo/RegisterPropertyChangedCallbackDemo.xaml

<Page
    x:Class="Windows10.Controls.BaseControl.DependencyObjectDemo.RegisterPropertyChangedCallbackDemo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Windows10.Controls.BaseControl.DependencyObjectDemo"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="Transparent">
        <Grid.Resources>
            <BeginStoryboard x:Name="sb">
                <Storyboard>
                    <DoubleAnimation 
                        EnableDependentAnimation="True"
                        Storyboard.TargetName="rect1" 
                        Storyboard.TargetProperty="Width" 
                        From="0" 
                        To="200" 
                        Duration="0:0:3">
                    </DoubleAnimation>
                </Storyboard>
            </BeginStoryboard>
        </Grid.Resources>
        
        <StackPanel Margin="10 0 10 10">

            <Rectangle Name="rect1" Height="50" Fill="Red" HorizontalAlignment="Left" Margin="5" />

            <TextBlock Name="lblMsg" Margin="5" />

        </StackPanel>
    </Grid>
</Page>

Controls/BaseControl/DependencyObjectDemo/RegisterPropertyChangedCallbackDemo.xaml.cs

/*
 * DependencyObject - 依赖对象(可以在 DependencyObject 上定义 DependencyProperty)
 *     RegisterPropertyChangedCallback() - 为指定的依赖属性注册一个变化回调
 *     UnregisterPropertyChangedCallback() - 取消 RegisterPropertyChangedCallback() 的注册
 *     
 * 
 * 本例用于演示如何监听 DependencyObject 的依赖属性的变化
 */

using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Shapes;

namespace Windows10.Controls.BaseControl.DependencyObjectDemo
{
    public sealed partial class RegisterPropertyChangedCallbackDemo : Page
    {
        private long _token = -1;

        public RegisterPropertyChangedCallbackDemo()
        {
            this.InitializeComponent();
        }

        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
            // 为 WidthProperty 注册一个变化回调(返回值是一个 token 用于取消注册用)
            _token = rect1.RegisterPropertyChangedCallback(Rectangle.WidthProperty, WidthChanged);
            
            base.OnNavigatedTo(e);
        }

        protected override void OnNavigatedFrom(NavigationEventArgs e)
        {
            // 为 WidthProperty 取消指定的变化回调(通过 token 来指定取消的是哪个变化回调)
            rect1.UnregisterPropertyChangedCallback(Rectangle.WidthProperty, _token);

            base.OnNavigatedFrom(e);
        }
        
        private void WidthChanged(DependencyObject sender, DependencyProperty prop)
        {
            double width = (double)sender.GetValue(prop);

            lblMsg.Text = $"width: {width}";
        }
    }
}



OK
[源码下载]