MyFessttoWord P8 ----PageViewModel

1,将Page更改为一个泛型的Page

 public class BasePage<VM> : Page
        where VM:BaseViewModel,new()
    {

        #region Private Member

        private VM mViewModel;//创建Page的ViewModel字段
        #endregion
        #region public properties

        /// <summary>
        /// the Animation is play when the page is first loaded
        /// </summary>
        public PageAnimation PageLoadAnimation { get; set; } = PageAnimation.SlideAndFadeInFromRight;

        /// <summary>
        /// The Animation is play when the page is unloaded
        /// </summary>
        public PageAnimation PageUnloadAnimation { get; set; } = PageAnimation.SlideAndFadeOutToLeft;

        /// <summary>
        /// the time any slide animation takes to complete
        /// </summary>
        public float SlideSeconds { get; set; } = 0.8f;

        public VM ViewModel  //设置和获取ViewModel
        {
            get
            { return mViewModel; }

            set
            {
                //if nothing has changed return;
                if (mViewModel == value) return;

                //Update the value
                mViewModel = value;

                //Set the datacontext for this page
                this.DataContext = mViewModel;//设置的时候,另Page的DataContext=特定的ViewModel

            }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Default Constructor
        /// </summary>
        public BasePage()
        {
            // If We are animate in ,hide to begin with
            if (this.PageLoadAnimation != PageAnimation.None)
                this.Visibility = Visibility.Collapsed;

            //Fire when Page loaded.
            this.Loaded += BasePage_LoadedAsync;

            //create a default view model
            this.ViewModel = new VM();//构造函数建立默认的ViewModel实例.?如何建立带参数的
        }

        #endregion
        #region Animation Load / Unload
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private async void BasePage_LoadedAsync(object sender, RoutedEventArgs e)
        {
            await AnimateIn();
        }
        #region AnimateIn
        public async Task AnimateIn()
        {
            //make sure we have something to do
            if (this.PageLoadAnimation == PageAnimation.None)
                return;

            //select the LoadSwitch
            switch (this.PageLoadAnimation)
            {
                //choose Slide And Fade In From Right
                case PageAnimation.SlideAndFadeInFromRight:
                    await this.SlideAndFadeInFromRight(SlideSeconds);
                    break;
            }

        }
        #endregion
        #region AnimateOut
        public async Task AnimateOut()
        {
            //make sure we have something to do
            if (this.PageUnloadAnimation == PageAnimation.None)
                return;

            //select the LoadSwitch
            switch (this.PageUnloadAnimation)
            {
                //choose Slide And Fade In From Right
                case PageAnimation.SlideAndFadeOutToLeft:
                    await this.SlideAndFadeOutToLeft(SlideSeconds);
                    break;
            }

        }
        #endregion
        #endregion
    }
  • 创建View Model的私有字段
  • ,并且属性化.
  • 并且在运行时,默认创建一个ViewModel
  • Page.DataContext=ViewModel

2,创建ViewMode类

/// <summary>
    /// ViewModel For Login Page
    /// </summary>
    public class LoginViewModel:BaseViewModel
    {
        #region Private Members

        #endregion

        #region Public Property

        /// <summary>
        /// the Email of the user
        /// </summary>
        public string Email { get; set; }

        public SecureString SecurePassword { get; set; }
        #endregion


        #region Commands

       public ICommand LoginCommand { get; set; }

        /// <summary>
        /// Indicate the login is runn
        /// </summary>
        public bool LoginIsRunning { get; set; }

        #endregion

        #region Constructor
        /// <summary>
        /// default construct
        /// </summary>
        public LoginViewModel()
        {


            //Create commands
            LoginCommand = new RelayParameterizedCommand(async (parameter) =>await Login(parameter) ) ;


        }
        #endregion
        /// <summary>
        /// Attempts to log the user in 
        /// </summary>
        /// <param name="parameter">the<see cref="SecureString"/>passed in from the view for the users password</param>
        /// <returns></returns>
        public async Task Login(object parameter)
        {
            await RunCommand(() => this.LoginIsRunning, async () =>
             {
                 //body of a async work!
                 await Task.Delay(5000);
                 var email = this.Email;
                 var password = this.SecurePassword.Unsecure();
             });

        }







    }
  • 创建属性对应页面的内容
    • public string Email { get; set; }//Email
    • public SecureString SecurePassword { get; set; } //密码
    • public ICommand LoginCommand { get; set; }//登录
    • public bool LoginIsRunning { get; set; }//登录运行中
  • 绑定LoginCommand
    • 建立参数化的RelayCommand类
  public class RelayParameterizedCommand : ICommand
    {
        /// <summary>
        ///  for action
        /// </summary>
        private Action<object> mAction;

        /// <summary>
        ///  constructor
        /// </summary>
        /// <param name="action"></param>
        public RelayParameterizedCommand(Action<object> action)
        {
            mAction = action;
        }

        //event when CanExecuteChanged,and doing Nothing
        public event EventHandler CanExecuteChanged = (send, e) => { };

        /// <summary>
        /// Judge if CanExecute the Command.
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        public bool CanExecute(object parameter)
        {
            return true;
        }

        /// <summary>
        /// do action of command
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(object parameter)
        {
            mAction(parameter);
        }
    }
        #endregion
  • 创建一个BaseViewModel的一个等待函数
protected async Task RunCommand(Expression<Func<bool>> updatingFlag,Func<Task> action)
        {
            //Expression.complie.get value
            if (updatingFlag.GetPropertyValue()) return;

            //Indicate we're running.
            updatingFlag.SetPropertyValue(true);
            try
            {
                await action();
            }
            finally
            {
                //set the property flag back to false now it's finished
                updatingFlag.SetPropertyValue(false);
            }

        }
主要
  • 用来避免异步任务多次执行.并且指示如果当前任务正在执行,则,updatingFlag=True,奖励了一个陷阱来防止任务多次执行.
  • finally来保证在一次时,释放掉锁.
  • 制作扩展函数利用反射来获取和设置属性的值.

public static class ExpressionHelpers
    {
        /// <summary>
        /// get a propertyValue use lambda
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="lambda"></param>
        /// <returns></returns>
        public static T GetPropertyValue<T>(this Expression<Func<T>> lambda)
        {
            return lambda.Compile().Invoke();
        }

        public static void SetPropertyValue<T>(this Expression<Func<T>> lambda,T value)
        {
            //comvert a lambada()=>some.Property, to some.Property.
            var expression = (lambda as LambdaExpression).Body as MemberExpression;
            var propertyInfo = (PropertyInfo)expression.Member;

            // use this
            var target = Expression.Lambda(expression.Expression).Compile().DynamicInvoke();
            propertyInfo.SetValue(target, value);
        }
    }

  • 从lambda中提取出来表达式本体://()=>value(LoginPage).LoginIsRunning===>value(LoginPage).LoginIsRunning
  • 作为MemberExpression,从中提取PropertyInfo
  • expression.Expression--->将value(LoginPage).LoginIsRunning--->获取包含属性的类对象.
  • 利用Expression.Lambda(expression.Expression)--->重构类对象指针.
  • Compile().DynamicInvoke()—>获取到其对象.

3,获取Password的值的信息

  • 建立扩展函数用于解析SecureString--->String
public static class SecureStringHelper
    {
        /// <summary>
        /// Unsecure a<see cref="SecureString"/> class
        /// </summary>
        /// <param name="securePassword">the Secure String</param>
        /// <returns></returns>
        public static string Unsecure(this SecureString secureString)
        {
            //makesure we has a secure string
            if (secureString == null) return string.Empty;

            var unmanagedString = IntPtr.Zero;

            //a way to get the secure string to a common string.
            try
            {
                //get a pointer of a secure string 
                unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureString);
                //copy it to the string .
                return Marshal.PtrToStringUni(unmanagedString);
            }
            finally
            {

                //Clean up any memory allocation
                Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
            }

        }
    }
  • 新建一个字符串指针//var unmanagedString = IntPtr.Zero
  • 将其指向安全字符串类//:unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(secureString);
  • 将指针转回字符串//Marshal.PtrToStringUni(unmanagedString);
  • 注意Finally强制释放指针Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);

  • 在LoginPage类中建立 映射,将密码框的SecurePassword转为SecurePassword.
  • SecureString SecurePassword => PasswordText.SecurePassword;

  • 建立一个IhaveSecure接口,从而避免将整个页面信息暴露.
  • 然后再页面的CommandParameter中,将整个xaml类赋值过去

4,将Login按钮添加一个Spinner功能

  • 新建一个等待的TextBlock Style

<Style TargetType="{x:Type TextBlock}" x:Key="SpinnerText" BasedOn="{StaticResource BaseTextBlockStyle}">
        <Style.Resources>
            <Storyboard x:Key="sb" >
                <DoubleAnimation Storyboard.TargetProperty="(UIElement.RenderTransform).(RotateTransform.Angle)"
                                 From="0"
                                 To="360"
                                 Duration="0:0:2"
                                 RepeatBehavior="Forever"/>
            </Storyboard>
        </Style.Resources>

        <Setter Property="FontFamily" Value="{StaticResource FontAwesome}"/>
        <Setter Property="Text" Value="&#xf110;"/>
        <Setter Property="RenderTransform">
            <Setter.Value>
                <RotateTransform/>
            </Setter.Value>
        </Setter>
        <Setter Property="RenderTransformOrigin" Value="0.5 0.5"/>
        <Style.Triggers>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self},Path=IsVisible}" Value="True">
                <DataTrigger.EnterActions>
                    <BeginStoryboard x:Name="beginSb"  Storyboard="{StaticResource sb}"/>
                </DataTrigger.EnterActions>
                <DataTrigger.ExitActions>
                    <RemoveStoryboard BeginStoryboardName="beginSb"/>
                </DataTrigger.ExitActions>
            </DataTrigger>
        </Style.Triggers>
    </Style>

  • 在FontAweSome网站下载字体.
  • 并且讲述了如何查看每个图标的字体代码---&#xff10;类似这种,在图标里面查找spin,然后右击该图标选择查看类型.然后在跳出来的网页源代码里面,点开i开头的东西,然后点击before,
  • image
  • 添加字体image
  • 添加资源
  • <FontFamily x:Key="FontAwesome">pack://application;,,,/Fonts/#FontAwesome</FontFamily>

  • 引用字体,及字体中的某个图标值
  • <Setter Property="FontFamily" Value="{StaticResource FontAwesome}"/>
            <Setter Property="Text" Value="&#xf110;"/>//注意格式.

  • 注意创建动画的效果.


5,button按钮中制作LoginIsRunning连接

<ControlTemplate TargetType="{x:Type ButtonBase}">
                    <Border x:Name="border"
                            CornerRadius="10"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}"
                            Background="{TemplateBinding Background}"
                            SnapsToDevicePixels="True">
                        <Grid>
                            <TextBlock Text="{TemplateBinding Content}"
                                   Focusable="False"
                                   FontFamily="{TemplateBinding FontFamily}"
                                   FontSize="{TemplateBinding FontSize}"
                                   HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                   Margin="{TemplateBinding Padding}"
                                   SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                   VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                   Visibility="{TemplateBinding local:IsBusyProperty.Value, Converter={local:BooleanToVisibilityConverter}}"
                                       />

                            <TextBlock Style="{StaticResource SpinnerText}"
                                   Focusable="False"
                                   FontSize="{TemplateBinding FontSize}"
                                   HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                                   Margin="{TemplateBinding Padding}"
                                   SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
                                   VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                                   Visibility="{TemplateBinding local:IsBusyProperty.Value, Converter={local:BooleanToVisibilityConverter},ConverterParameter=True}"/>
                        </Grid>



                    </Border>
                    <ControlTemplate.Triggers>
                        <EventTrigger RoutedEvent="MouseEnter">
                            <BeginStoryboard>
                                <Storyboard>
                                    <ColorAnimation To="{StaticResource WordBlue}" Duration="0:0:0.3" Storyboard.TargetName="border" Storyboard.TargetProperty="Background.Color" />
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                        <EventTrigger RoutedEvent="MouseLeave">
                            <BeginStoryboard>
                                <Storyboard>
                                    <ColorAnimation From="{StaticResource WordBlue}" Duration="0:0:0.3" Storyboard.TargetName="border" Storyboard.TargetProperty="Background.Color" />
                                </Storyboard>
                            </BeginStoryboard>
                        </EventTrigger>
                        <Trigger Property="IsEnabled" Value="False">
                            <Setter Property="Background" TargetName="border" Value="{StaticResource ForegroundDarkBrush}"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>

  1. 建立依赖性属性 IsBusyProperty.Value
  2. 建立一个泛型的依赖项属性生成类

  

public abstract class BaseAttachedProperty <Parent,Property>
        where Parent :  new()
    {
        #region Public event
        /// <summary>
        /// Fired when the value changes, 
        /// </summary>
        public event Action<DependencyObject, DependencyPropertyChangedEventArgs> ValueChanged = (sender, e) => { };


        /// <summary>
        /// Fired when the value changes, even when the value is the same
        /// </summary>
        public event Action<DependencyObject, object> ValueUpdated = (sender, value) => { };
        #endregion
        #region Public Properties
        /// <summary>
        /// A singleton instance of our parent class; 使用这个办法是可以一种使用泛型模板制造静态实例的经典案列.
        /// </summary>
        public static Parent Instance { get; private set; } = new Parent();
        #endregion

        #region Attaced Property Definitions

        public static readonly DependencyProperty Value = DependencyProperty.RegisterAttached("Value",
                                                        typeof(Property),
                                                        typeof(BaseAttachedProperty<Parent, Property>),
                                                        new UIPropertyMetadata(default(Property),new PropertyChangedCallback(OnValuePropertyChanged),
                                                            new CoerceValueCallback(OnValuePropertyUpdated)
                                                            )
            );

        public static Property GetValue(DependencyObject d) => (Property)d.GetValue(Value);

        public static void SetValue(DependencyObject d, Property value) => d.SetValue(Value, value);
        #endregion

        #region FireFunctions
        /// <summary>
        /// when value changed ,trigger it
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>

        private static void OnValuePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //Fire the class Changed
            (Instance as BaseAttachedProperty<Parent, Property>)?.OnValueChanged(d, e);

            //Fire the Listerner Changed
            (Instance as BaseAttachedProperty<Parent, Property>)?.ValueChanged(d, e);

        }

        /// <summary>
        /// when value updated ,do this
        /// </summary>
        /// <param name="d"></param>
        /// <param name="baseValue"></param>
        /// <returns></returns>
        private static object OnValuePropertyUpdated(DependencyObject d, object value)
        {
            //Fire the class Changed
            (Instance as BaseAttachedProperty<Parent, Property>)?.OnValueUpdated(d, value);

            //Fire the Listerner Changed
            (Instance as BaseAttachedProperty<Parent, Property>)?.ValueUpdated(d, value);
            return value;
        }



        #endregion

        #region Local Fun
        /// <summary>
        /// Func Value Changed doing sth.
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        public virtual void OnValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {

        }
        /// <summary>
        /// ValueUpdated Fun doing sth.
        /// </summary>
        /// <param name="d"></param>
        /// <param name="value"></param>
        public virtual void OnValueUpdated(DependencyObject d, object value)
        {

        }
        #endregion


    }

  • 定义ValueChanged和ValueUpdate事件
  • 建立Singlton类的实例和依赖性属性Value
  • 建立i两个虚拟函数,用于在事件触发时优先调用.

   将button的Visibility进行绑定之后,使用Convert.

public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (parameter == null)
                return (bool)value == false ? Visibility.Visible : Visibility.Hidden;

            return (bool)value == true ? Visibility.Visible : Visibility.Hidden;

        }

image

结果:

在程序中查看密码和Email地址:

image

posted @ 2021-02-25 00:17  frogkiller  阅读(71)  评论(0编辑  收藏  举报