当前位置:网站首页 / WPF / 正文

2020 WPF 取经之路第9天 - MahApps 主题\对话框\flyout整理

时间:2020年04月16日 | 作者 : aaronyang | 分类 : WPF | 浏览: 737次 | 评论 0

以下用法都针对4.0 framework的1.6.5.1版本的 mah

demo1 主题

默认测试,界面

 <StackPanel Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center">
            <RadioButton>radio测试1</RadioButton>
            <RadioButton>radio测试2</RadioButton>
            <CheckBox>check测试</CheckBox>
        </StackPanel>

方式1,在App.xaml改

image.png

Cobalt

image.png

换成blue

image.png

Blue可以换的值:

“Red”, “Green”, “Blue”, “Purple”, “Orange”, “Lime”, “Emerald”, “Teal”, “Cyan”, “Cobalt”, “Indigo”, “Violet”, “Pink”, “Magenta”, “Crimson”, “Amber”, “Yellow”, “Brown”, “Olive”, “Steel”, “Mauve”, “Taupe”, “Sienna”

下方的BaseLight可以换

“BaseLight”, “BaseDark”


运行时候更换

 public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            Tuple<AppTheme, Accent> appStyle = ThemeManager.DetectAppStyle(Application.Current);

            ThemeManager.ChangeAppStyle(Application.Current,
                                        ThemeManager.GetAccent("Green"),
                                        ThemeManager.GetAppTheme("BaseDark")); 

            base.OnStartup(e);
        }

    }

image.png

当然也可以放在单独的window的resource中,自己尝试


DEMO2 设置发光与winform测试

GlowBrush="Red"

添加引用

image.png

<Controls:MetroWindow x:Class="AyMahAppsStudy.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro" Title="AY测试" Height="600" Width="800" TitleForeground="Red" GlowBrush="Red" xmlns:wf="clr-namespace:System.Windows.Forms;assembly=System.Windows.Forms" xmlns:wfi="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration">
    <Controls:MetroWindow.TitleTemplate>
        <DataTemplate>
            <Grid></Grid>
        </DataTemplate>
    </Controls:MetroWindow.TitleTemplate>
    <Controls:MetroWindow.RightWindowCommands>
        <Controls:WindowCommands ShowLastSeparator="False">
            <Button Content="settings" />
            <Button>
                <StackPanel Orientation="Horizontal">
                    <Rectangle Width="20" Height="20" Fill="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Foreground}">
                    </Rectangle>
                    <TextBlock Margin="4 0 0 0" VerticalAlignment="Center" Text="deploy cupcakes" />
                </StackPanel>
            </Button>
        </Controls:WindowCommands>
    </Controls:MetroWindow.RightWindowCommands>
    
    <Grid Background="Transparent" Margin="0,-30,0,0">
        <Grid.RowDefinitions>
            <RowDefinition Height="30" />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid>
            <Grid.Background>
                <LinearGradientBrush>
                    <LinearGradientBrush.GradientStops>
                        <GradientStop Color="Red" Offset="0.2"></GradientStop>
                        <GradientStop Color="Green" Offset="0.7"></GradientStop>
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Grid.Background>
            <TextBlock Text="自定义标题,不影响任务栏显示" Margin="5,0,0,0" Foreground="White" HorizontalAlignment="Left" VerticalAlignment="Center" />
        </Grid>

        <StackPanel Grid.Row="1" HorizontalAlignment="Center" VerticalAlignment="Center">
            <RadioButton>radio测试1</RadioButton>
            <RadioButton>radio测试2</RadioButton>
            <CheckBox>check测试</CheckBox>
            <StackPanel>
                <wfi:WindowsFormsHost>
                    <wf:Label x:Name="wfLabel" Text="winForm控件在此" />
                </wfi:WindowsFormsHost>

                <wfi:WindowsFormsHost>
                    <wf:Button x:Name="wfButton" Text="确定" Click="wfButton_Click" />
                </wfi:WindowsFormsHost>

                <Button Content="Button" Margin="10" Name="button1" />
            </StackPanel>
        </StackPanel>
        
    </Grid>
</Controls:MetroWindow>

image.png

答案是,winform控件是显示的,这在我的框架不行的,因为wpf的 allowtransparent=true时候,winform控件看不见但是可以触碰的


目前发现 圆角窗体咋实现未知

按照常识,你可以allowtransparent=true,windowstyle=none,自己去 弄一个窗体解决吧



Demo3 对话框

顶部引用这个空间

image.png

后台代码:

      private void wfButton_Click(object sender, EventArgs e)
        {
            this.ShowProgressAsync("Please wait...", "Progress message");
        }

image.png

大家也发现了,winform控件置顶的问题,

所以wpf程序如果做触屏的程序,用usercontrol方式做弹窗问题多多

想要弹窗遮住winform的,用popup或者新的window


但是他也有一个好思路,用拓展方法实现,这样可以快速解决传入owner的问题

image.png

大致看完了,xaml.cs弹窗就拓展方法,如果想vm弹窗

首先窗体顶部  xmlns:Dialog="clr-namespace:MahApps.Metro.Controls.Dialogs;assembly=MahApps.Metro"

Dialog:DialogParticipation.Register="{Binding}"


这个附加属性,会把VM和当前window对象绑定

接着在vm中,放入这个接口

image.png

接着xaml.cs会以最传统方式,把 接口传入

image.png

这个接口的实例就会在

那个键值的VM和窗体的集合, 通过VM找到metrowindow

image.png

image.png

image.png

有了metrowindow,就可以像在xaml.cs中那样调用拓展方法和传参了


DEMO4 Flyout

metrowindow有个属性Flyout,类似dockpanel或者splitview,一个容器控件

使用层次如下,(由于 ay下载的源码,很多编译报错,就不管了,理解设计就行了)

image.png

控制显示和隐藏,设置IsOpen属性

image.png

所以它打开和关闭都是传入索引


查看MetroWindow的样式源码

image.png

flyout内容层,在PART_Content内容之上,

下方有个PART_FlyoutModal的矩形,

肯定flyout显示的时候,矩形也显示,挡住底部的窗体的内容,

下方还有个metrothumb,是矩形之上的,表示,弹出flyout时候,非flyout内容区域,可以有类似WindowTitlebar的效果,可以拖动窗体移动,双击放大缩小。


接下来有个ContentControl就是显示Flyouts的了。


flyout之后,是dialog层

分3块, 未激活的dialog内容层,遮罩动画层,激活的dialog内容层

image.png

在整个以上所有内容之上,盖上border和右下角的resizegrid调整窗体大小的控件

image.png


遮罩动画

image.png


窗体上的Icon显示,是用MultiFrameImage控件

看了代码,

BitmapFrame有个Decoder属性,decoder.frames,是当前图片包括的帧,比如gif是大于1帧的,这个控件显示多帧中的

第一个图,当然这个图,是按照 最大尺寸,且每size颜色深度最高的那个帧,返回显示


窗体模板两个,默认WindowTemplateKey,如果title是居中的,则使用CenterWindowTemplateKey模板

当然,窗体内容层,还有左侧按钮区域和右侧按钮区域的

image.png




DEMO5 其他知识

还有个自定义控件,模板中的控件名字写法,

image.png

可以直接把名字放在当下的控件中


还有个弹window释放的写法

             private FlyoutDemo flyoutDemo;
        private void LaunchFlyoutDemo(object sender, RoutedEventArgs e)
        {
            if (flyoutDemo == null)
            {
                flyoutDemo = new FlyoutDemo();
                flyoutDemo.Closed += (o, args) => flyoutDemo = null;
            }
            flyoutDemo.Launch();
        }

FlyoutDemo是个窗体,如果不这样写,多次点那个按钮,弹窗,会报错的,说上次的窗体未释放,这里closed释放的。


当然还有一些默认值,比如定义依赖属性,默认值使用default(类型)

image.png

还有回调写法,新值不等于旧值

      private static void ToggleFullScreenPropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e){
    var metroWindow = (MetroWindow)dependencyObject;
            if (e.OldValue != e.NewValue)
            {
                var fullScreen = (bool)e.NewValue;
                if (fullScreen)
                {
                  
                }
                else
                {
                    
                }
            }
            }

最简单的单例转换器写法

using System;
using System.Windows.Data;

namespace MahApps.Metro.Converters
{
    /// <summary>
    /// Converts the value from true to false and false to true.
    /// </summary>
    public sealed class IsNullConverter : IValueConverter
    {
        private static IsNullConverter _instance;
        static IsNullConverter()
        {
        }

        private IsNullConverter()
        {
        }

        public static IsNullConverter Instance
        {
            get { return _instance ?? (_instance = new IsNullConverter()); }
        }

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return null == value;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return Binding.DoNothing;
        }
    }
}

使用

Binding="{Binding RelativeSource={RelativeSource Self}, Path=Icon, Mode=OneWay, Converter={x:Static Converters:IsNullConverter.Instance}}"

触发器取反写法,使用转换器

  <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Icon, Mode=OneWay, Converter={x:Static Converters:IsNullConverter.Instance}}" Value="False">
                <Setter Property="IconTemplate">


当然如果你的转换器带属性,就不能单例了

   [ValueConversion(typeof(Thickness), typeof(double), ParameterType = typeof(ThicknessSideType))]
    public class ThicknessToDoubleConverter : IValueConverter
    {
        public ThicknessSideType TakeThicknessSide { get; set; }

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is Thickness)
            {
                // yes, we can override it with the parameter value
                if (parameter is ThicknessSideType)
                {
                    this.TakeThicknessSide = (ThicknessSideType)parameter;
                }
                var orgThickness = (Thickness)value;
                switch (this.TakeThicknessSide)
                {
                    case ThicknessSideType.Left:
                        return orgThickness.Left;
                    case ThicknessSideType.Top:
                        return orgThickness.Top;
                    case ThicknessSideType.Right:
                        return orgThickness.Right;
                    case ThicknessSideType.Bottom:
                        return orgThickness.Bottom;
                    default:
                        return default(double);
                }
            }

            return default(double);
        }

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

顶部也使用了这个强类型特性,  [ValueConversion(typeof(Thickness), typeof(double), ParameterType = typeof(ThicknessSideType))]

xmlns:Converters="clr-namespace:MahApps.Metro.Converters"
    <Converters:ThicknessBindingConverter x:Key="ThicknessBindingConverter" />



还有个知识点 ApplicationSettingsBase运用

在设计阶段,您可以使用两种方式来加入应用程序设定:第一种方式是通过「项目设计器」的「设置」页面;第二种方式是通过窗体或控件的“属性”窗口以便将某一个属性直接绑定到某一个应用程序设定。在执行阶段,则可以通过ApplicationSettingsBase类别来存取应用程序设定。

这在metrowindow有IWindowPlacementSettings

这里学习模拟下:参考文章

需要添加类库System.Configuration

新建接口

    public interface IUserSettings : INotifyPropertyChanged
    {
        void Register<T>(string name, T defaultValue);
        bool Contains(string name);
        T Get<T>(string name, T defaultValue);
        void Set<T>(string name, T value);

        void Reload();
        void Save();
        void Upgrade();

    }

我们保留这3个名字的方法

image.png

其他随意 添加register,是否包含contain,获取get,修改set

using System;
using System.ComponentModel;

namespace AyMahAppsStudy
{
    public interface IUserSettings : INotifyPropertyChanged
    {
        void Register<T>(string name, T defaultValue);
        bool Contains(string name);
        T Get<T>(string name, T defaultValue);
        void Set<T>(string name, T value);

        void Reload();
        void Save();
        void Upgrade();

    }


    public sealed class UserSettings : System.Configuration.ApplicationSettingsBase, IUserSettings
    {

        private static readonly bool ThrowOnErrorDeserializing = false, ThrowOnErrorSerializing = false;

        private static readonly System.Configuration.SettingsAttributeDictionary SettingsAttributes = new System.Configuration.SettingsAttributeDictionary() {
            {typeof(System.Configuration.UserScopedSettingAttribute), new System.Configuration.UserScopedSettingAttribute()}
        };

        private System.Configuration.SettingsProvider provider;

        private UserSettings()
        {
        }

        private static IUserSettings defaultInstance = ((UserSettings)System.Configuration.ApplicationSettingsBase.Synchronized(new UserSettings()));
        public static IUserSettings Instance
        {
            get
            {
                return defaultInstance;
            }
        }

        public void Register<T>(string name, T defaultValue)
        {
            if (name == null || name.Trim().Length == 0)
                throw new ArgumentNullException("name");
            var property = this.Properties[name];
            if (property == null)
                this.CreateSettingsProperty(name, typeof(T), defaultValue);
        }

        public bool Contains(string name)
        {
            if (name == null || name.Trim().Length == 0)
                throw new ArgumentNullException("name");
            var property = this.Properties[name];
            return property != null;
        }

        public void Set<T>(string name, T value)
        {
            if (this.Contains(name) == false)
                this.Register<T>(name, value);
            this[name] = value;
        }

        public T Get<T>(string name, T defaultValue)
        {
            if (name == null || name.Trim().Length == 0)
                throw new ArgumentNullException("name");
            if (this.Contains(name))
            {
                return (T)(this[name] ?? defaultValue);
            }
            else
            {
                this.CreateSettingsProperty(name, typeof(T), defaultValue);
                var val = this[name];
                //if(val == null) this.Remove(name);                
                return (T)(val ?? defaultValue);
            }
        }

        public void Remove(string name)
        {
            if (name == null || name.Trim().Length == 0)
                throw new ArgumentNullException("name");
            this.PropertyValues.Remove(name);
            this.Properties.Remove(name);
        }

        private void CreateSettingsProperty(string name, Type propertyType, object defaultValue)
        {
            var property = new System.Configuration.SettingsProperty(name, propertyType, this.Provider, false, defaultValue,
                this.GetSerializeAs(propertyType), SettingsAttributes, ThrowOnErrorDeserializing, ThrowOnErrorSerializing);
            this.Properties.Add(property);
        }

        private System.Configuration.SettingsSerializeAs GetSerializeAs(Type type)
        {
            TypeConverter converter = TypeDescriptor.GetConverter(type);
            bool flag = converter.CanConvertTo(typeof(string));
            bool flag2 = converter.CanConvertFrom(typeof(string));
            if (flag && flag2)
            {
                return System.Configuration.SettingsSerializeAs.String;
            }
            return System.Configuration.SettingsSerializeAs.Xml;
        }

        private System.Configuration.SettingsProvider Provider
        {
            get
            {
                if (this.provider == null && (this.provider = this.Providers["LocalFileSettingsProvider"]) == null)
                {
                    this.provider = new System.Configuration.LocalFileSettingsProvider();
                    this.provider.Initialize(null, null);
                    this.Providers.Add(this.provider);
                }
                return this.provider;
            }
        }

    }

}

使用测试

 UserSettings.Instance.Set<int>("TestValue", 123123);
            UserSettings.Instance.Save();
           var _1= UserSettings.Instance.Get<int>("TestValue", 0);
            MessageBox.Show(_1.ToString());

这里肯定弹出123123

继续测试

image.png

重启项目,值依然存在、

测试一个不存在的 名字

image.png

返回0默认值了。


接下来看下metrowindow的,他是记录窗体上次位置的。

image.png

这个方法返回一个WindowApplicationSettings类

她的代码如下:

using ControlzEx.Standard;
using System.Configuration;
using System.Windows;

namespace MahApps.Metro.Controls
{
    public interface IWindowPlacementSettings
    {
#pragma warning disable 618
        WINDOWPLACEMENT Placement { get; set; }
#pragma warning restore 618

        /// <summary>
        /// Refreshes the application settings property values from persistent storage.
        /// </summary>
        void Reload();

        /// <summary>
        /// Upgrades the application settings on loading.
        /// </summary>
        bool UpgradeSettings { get; set; }

        /// <summary>
        /// Updates application settings to reflect a more recent installation of the application.
        /// </summary>
        void Upgrade();

        /// <summary>
        /// Stores the current values of the settings properties.
        /// </summary>
        void Save();
    }

    /// <summary>
    /// this settings class is the default way to save the placement of the window
    /// </summary>
    internal class WindowApplicationSettings : ApplicationSettingsBase, IWindowPlacementSettings
    {
        public WindowApplicationSettings(Window window)
            : base(window.GetType().FullName)
        {
            
        }

#pragma warning disable 618
        [UserScopedSetting]
        public WINDOWPLACEMENT Placement
        {
            get
            {
                if (this["Placement"] != null)
                {
                    return ((WINDOWPLACEMENT)this["Placement"]);
                }
                return null;
            }
            set { this["Placement"] = value; }
        }
#pragma warning restore 618

        /// <summary>
        /// Upgrades the application settings on loading.
        /// </summary>
        [UserScopedSetting]
        public bool UpgradeSettings
        {
            get
            {
                try
                {
                    if (this["UpgradeSettings"] != null)
                    {
                        return (bool)this["UpgradeSettings"];
                    }
                }
                catch (ConfigurationErrorsException ex)
                {
                    string filename = null;
                    while (ex != null && (filename = ex.Filename) == null)
                    {
                        ex = ex.InnerException as ConfigurationErrorsException;
                    }
                    throw new MahAppsException(string.Format("The settings file '{0}' seems to be corrupted", filename ?? "<unknown>"), ex);
                }
                return true;
            }
            set { this["UpgradeSettings"] = value; }
        }
    }
}

这个看不懂的就算了,我不推荐用这个,网上问题也挺多的。







关于窗体bug解决汇总

在metrowindow界面中不显示winform的东西,添加以下内容 就可以正确显示WindowsFormsHost了

 xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"

 xmlns:behaviours="clr-namespace:MahApps.Metro.Behaviours;assembly=MahApps.Metro"

<i:Interaction.Behaviors>

  <behaviours:BorderlessWindowBehavior ResizeWithGrip="True" AllowsTransparency="False" />

</i:Interaction.Behaviors>


ay测试,metrowindow提供的发光行为,是在后台设置的,前台xaml设置有bug

using System.ComponentModel;
using System.Windows;
using System.Windows.Interactivity;
using MahApps.Metro.Controls;

namespace Taurus.Framework.Behaviors
{
    public class CustomGlowWindowBehavior : Behavior<MetroWindow>
    {
        private GlowWindow left;
        private GlowWindow right;
        private GlowWindow top;
        private GlowWindow bottom;

        protected override void OnAttached()
        {
            base.OnAttached();
            this.AssociatedObject.Loaded += new RoutedEventHandler(this.AssociatedObjectOnLoaded);
        }

        private void AssociatedObjectOnLoaded(object sender, RoutedEventArgs routedEventArgs)
        {
            MetroWindow metroWindow = this.AssociatedObject as MetroWindow;
            if (metroWindow != null && (metroWindow.UseNoneWindowStyle/* || metroWindow.GlowBrush == null*/))
                return;
            this.left = new GlowWindow(this.AssociatedObject, GlowDirection.Left);
            this.right = new GlowWindow(this.AssociatedObject, GlowDirection.Right);
            this.top = new GlowWindow(this.AssociatedObject, GlowDirection.Top);
            this.bottom = new GlowWindow(this.AssociatedObject, GlowDirection.Bottom);
            this.Show();
            this.Update();

            metroWindow.LocationChanged += (s, e) => this.Update();
            metroWindow.SizeChanged += (s, e) => this.Update();

            if (metroWindow == null || !metroWindow.WindowTransitionsEnabled)
            {
                this.SetOpacityTo(1.0);
            }
            else
            {
                this.StartOpacityStoryboard();
                this.AssociatedObject.IsVisibleChanged += new DependencyPropertyChangedEventHandler(this.AssociatedObjectIsVisibleChanged);
                this.AssociatedObject.Closing += (CancelEventHandler) ((o, args) =>
                {
                    if (args.Cancel)
                        return;
                    this.AssociatedObject.IsVisibleChanged -= new DependencyPropertyChangedEventHandler(this.AssociatedObjectIsVisibleChanged);
                });
            }
        }

        private void AssociatedObjectIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if (!this.AssociatedObject.IsVisible)
                this.SetOpacityTo(0.0);
            else
                this.StartOpacityStoryboard();
        }

        /// <summary>
        /// Updates all glow windows (visible, hidden, collapsed)
        /// 
        /// </summary>
        private void Update()
        {
            if (this.left == null || this.right == null || (this.top == null || this.bottom == null))
                return;
            this.left.Update();
            this.right.Update();
            this.top.Update();
            this.bottom.Update();
        }

        /// <summary>
        /// Sets the opacity to all glow windows
        /// 
        /// </summary>
        private void SetOpacityTo(double newOpacity)
        {
            if (this.left == null || this.right == null || (this.top == null || this.bottom == null))
                return;
            this.left.Opacity = newOpacity;
            this.right.Opacity = newOpacity;
            this.top.Opacity = newOpacity;
            this.bottom.Opacity = newOpacity;
        }

        /// <summary>
        /// Starts the opacity storyboard 0 -&gt; 1
        /// 
        /// </summary>
        private void StartOpacityStoryboard()
        {
            if (this.left == null || this.left.OpacityStoryboard == null || (this.right == null || this.right.OpacityStoryboard == null) || (this.top == null || this.top.OpacityStoryboard == null || (this.bottom == null || this.bottom.OpacityStoryboard == null)))
                return;
            this.left.BeginStoryboard(this.left.OpacityStoryboard);
            this.right.BeginStoryboard(this.right.OpacityStoryboard);
            this.top.BeginStoryboard(this.top.OpacityStoryboard);
            this.bottom.BeginStoryboard(this.bottom.OpacityStoryboard);
        }

        /// <summary>
        /// Shows all glow windows
        /// 
        /// </summary>
        private void Show()
        {
            this.left.Show();
            this.right.Show();
            this.top.Show();
            this.bottom.Show();
        }
    }
}






 AYUI       www.ayjs.net      AY         杨洋原创编写,请不要转载谢谢















推荐您阅读更多有关于“WPF4.5,”的文章

猜你喜欢

额 本文暂时没人评论 来添加一个吧

发表评论

必填

选填

选填

必填

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

  查看权限

合肥科大智能常年招聘.NET,Java,Web前端,有想找想换工作的私聊我AY唯一QQ:875556003和AY交流

抖音号:wpfui,可以看到我的很多作品效果

AYUI8社区版Github地址:前往获取

作者:杨洋(AaronYang简称AY,安徽六安人)目前是个人,还没公司AY唯一QQ:875556003和AY交流

高中学历,2015年1月17日开始,兴趣学习研究WPF,目前工作繁忙,不接任何活

声明:AYUI7个人与商用免费,源码可购买。部分DEMO不免费.AY主要靠卖技术服务挣钱

不是从我处购买的ayui7源码,我不提供任何技术服务,如果你举报从哪里买的,我可以帮你转正为我的客户,并送demo

查看捐赠

AYUI7.X MVC教程 更新如下:

第一课 第二课 程序加密教程

vs2015 企业版密钥HM6NR-QXX7C-DFW2Y-8B82K-WTYJV

vs2017 企业版密钥NJVYC-BMHX2-G77MM-4XJMR-6Q8QF

标签列表