首页 > 其他分享 >Avalonia实现Visual Studio风格标题栏的方法

Avalonia实现Visual Studio风格标题栏的方法

时间:2024-04-17 10:44:40浏览次数:17  
标签:Style Default 标题栏 System value Visual Studio public ModernStyleType

     Visual Studio风格的标题栏可以更节省屏幕空间,个人认为其实比Ribbonbar和传统菜单都要更先进一些,更紧凑,利用效率更高。

 我在AvaloniaSamples项目中添加了一个这种Demo,展示了如何在Avalonia 11中分别实现经典风格、Macos风格和Visual Studio风格的标题栏:

 

 

 

    关键点就在于MainWindow.axaml和MainWindowViewModel.cs中。

using Avalonia.Platform;
using System.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace AvaloniaVisualStudioTitleBar.ViewModels
{
    public class MainWindowViewModel : ViewModelBase
    {
        public string AppName => "Avalonia.Visual Studio Title Bar Demo";

        /// <summary>
        /// Gets the modern style.
        /// </summary>
        /// <value>The modern style.</value>
        public ModernStyleDataModel ModernStyle { get; private set; } = new ModernStyleDataModel();

        public object[] Styles => Enum.GetValues(typeof(ModernStyleType)).Cast<object>().ToArray();

        public object Style
        {
            get => ModernStyle.Style;
            set => ModernStyle.Style = value != null ? (ModernStyleType)value :ModernStyleType.Default;
        }

    }


    #region Demo Support
    /// <summary>
    /// Enum ModernStyleType
    /// </summary>
    public enum ModernStyleType
    {
        /// <summary>
        /// The default
        /// </summary>
        Default,

        /// <summary>
        /// The windows metro
        /// </summary>
        WindowsMetro,

        /// <summary>
        /// The mac os
        /// </summary>
        MacOS,

        /// <summary>
        /// The classic
        /// </summary>
        Classic
    }

    #region Attributes    
    /// <summary>
    /// Class DependsOnPropertyAttribute.
    /// Implements the <see cref="Attribute" />
    /// </summary>
    /// <seealso cref="Attribute" />
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true)]
    public class DependsOnPropertyAttribute : Attribute
    {
        /// <summary>
        /// The dependency properties
        /// </summary>
        public readonly string[] DependencyProperties;

        /// <summary>
        /// Initializes a new instance of the <see cref="DependsOnPropertyAttribute"/> class.
        /// </summary>
        /// <param name="propertyNames">The property names.</param>
        public DependsOnPropertyAttribute(params string[] propertyNames)
        {
            DependencyProperties = propertyNames;
        }
    }
    #endregion

    #region ReactiveObject
    /// <summary>
    /// Interface IReactiveObject
    /// Implements the <see cref="BluePrint.Common.ComponentModel.INotifyPropertyChanged" />
    /// Implements the <see cref="BluePrint.Common.ComponentModel.INotifyPropertyChanging" />
    /// </summary>
    /// <seealso cref="BluePrint.Common.ComponentModel.INotifyPropertyChanged" />
    /// <seealso cref="BluePrint.Common.ComponentModel.INotifyPropertyChanging" />
    public interface IReactiveObject : INotifyPropertyChanged
    {
        /// <summary>
        /// Raises the property changed.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        void RaisePropertyChanged(string propertyName);
    }

    public static class TypeExtensions
    {
        /// <summary>
        /// Gets the custom attributes.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="memberInfo">The member information.</param>
        /// <param name="inherit">if set to <c>true</c> [inherit].</param>
        /// <returns>T[].</returns>
        public static T[] GetCustomAttributes<T>(this MemberInfo memberInfo, bool inherit = true)
            where T : Attribute
        {
            var Attrs = memberInfo.GetCustomAttributes(typeof(T), inherit);

            if (Attrs != null && Attrs.Length > 0)
            {
                return Attrs.Cast<T>().ToArray();
            }

            return new T[0];
        }
    }

    /// <summary>
    /// Class ReactiveObject.
    /// Implements the <see cref="BluePrint.Common.ComponentModel.INotifyPropertyChanged" />
    /// </summary>
    /// <seealso cref="BluePrint.Common.ComponentModel.INotifyPropertyChanged" />
    public class ReactiveObject : IReactiveObject
    {
        #region Properties        
        private Stack<string> ProcessStack = new Stack<string>();
        #endregion

        #region Constructor
        /// <summary>
        /// Initializes a new instance of the <see cref="ReactiveObject"/> class.
        /// </summary>
        public ReactiveObject()
        {
            PropertyChanged += ProcessPropertyChanged;

            AutoCollectDependsInfo();
        }

        private static Dictionary<System.Type, Dictionary<string, List<string>>> MetaCaches = new Dictionary<Type, Dictionary<string, List<string>>>();

        /// <summary>
        /// Automatics the collect depends information.
        /// </summary>
        private void AutoCollectDependsInfo()
        {
            var type = GetType();
            if (MetaCaches.TryGetValue(type, out var cache))
            {
                return;
            }

            cache = new Dictionary<string, List<string>>();

            foreach (var property in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance))
            {
                foreach (var attr in property.GetCustomAttributes<DependsOnPropertyAttribute>())
                {
                    foreach (var name in attr.DependencyProperties)
                    {
                        if (!cache.TryGetValue(name, out var relevance))
                        {
                            relevance = new List<string>();
                            cache.Add(name, relevance);
                        }

                        relevance.Add(property.Name);
                    }
                }
            }

            if (cache.Count > 0)
            {
                MetaCaches[type] = cache;
            }
            else
            {
                MetaCaches[type] = null;
            }
        }

        /// <summary>
        /// Gets the cache.
        /// </summary>
        /// <param name="type">The type.</param>
        /// <returns>Dictionary&lt;System.String, List&lt;System.String&gt;&gt;.</returns>
        private static Dictionary<string, List<string>> GetCache(Type type)
        {
            MetaCaches.TryGetValue(type, out var cache);
            return cache;
        }
        #endregion

        #region Interfaces
        /// <summary>
        /// Occurs when a property value changes.
        /// </summary>
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Raises the property changed.
        /// </summary>
        /// <param name="propertyName">Name of the property.</param>
        public void RaisePropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }

        #endregion

        #region Methods                        
        /// <summary>
        /// Processes the property changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.PropertyChangedEventArgs"/> instance containing the event data.</param>
        protected virtual void ProcessPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (ProcessStack.Contains(e.PropertyName))
            {
                return;
            }

            var RelativeProperties = GetCache(GetType());

            if (RelativeProperties != null && RelativeProperties.TryGetValue(e.PropertyName, out var relevance))
            {
                try
                {
                    ProcessStack.Push(e.PropertyName);

                    foreach (var r in relevance)
                    {
                        RaisePropertyChanged(r);
                    }
                }
                finally
                {
                    ProcessStack.Pop();
                }
            }
        }

        #endregion
    }
    #endregion

    #region Extensions
    /// <summary>
    /// Class ReactiveObjectExtensions.
    /// </summary>
    public static class ReactiveObjectExtensions
    {
        /// <summary>
        /// Raises the and set if changed.
        /// </summary>
        /// <typeparam name="TObj">The type of the t object.</typeparam>
        /// <typeparam name="TRet">The type of the t ret.</typeparam>
        /// <param name="reactiveObject">The reactive object.</param>
        /// <param name="backingField">The backing field.</param>
        /// <param name="newValue">The new value.</param>
        /// <param name="propertyName">Name of the property.</param>
        /// <returns>TRet.</returns>
        public static TRet RaiseAndSetIfChanged<TObj, TRet>(this TObj reactiveObject,
            ref TRet backingField,
            TRet newValue,
            [CallerMemberName] string propertyName = null
            )
            where TObj : IReactiveObject
        {
            if (EqualityComparer<TRet>.Default.Equals(backingField, newValue))
            {
                return newValue;
            }

            backingField = newValue;
            reactiveObject.RaisePropertyChanged(propertyName);

            return newValue;
        }

        /// <summary>
        /// Raises the property changed.
        /// </summary>
        /// <typeparam name="TSender">The type of the t sender.</typeparam>
        /// <param name="reactiveObject">The reactive object.</param>
        /// <param name="propertyName">Name of the property.</param>
        public static void RaisePropertyChanged<TSender>(this TSender reactiveObject, [CallerMemberName] string propertyName = null)
            where TSender : IReactiveObject
        {
            if (propertyName is not null)
            {
                reactiveObject.RaisePropertyChanged(propertyName);
            }
        }
    }
    #endregion

    /// <summary>
    /// Class ModernStyleDataModel.
    /// Implements the <see cref="ReactiveObject" />
    /// </summary>
    /// <seealso cref="ReactiveObject" />
    public class ModernStyleDataModel : ReactiveObject
    {
        bool ExtendClientAreaToDecorationsHintCore = false;
        /// <summary>
        /// Gets or sets a value indicating whether [extend client area to decorations hint].
        /// </summary>
        /// <value><c>true</c> if [extend client area to decorations hint]; otherwise, <c>false</c>.</value>
        public bool ExtendClientAreaToDecorationsHint
        {
            get => ExtendClientAreaToDecorationsHintCore;
            set => this.RaiseAndSetIfChanged(ref ExtendClientAreaToDecorationsHintCore, value);
        }

        int ExtendClientAreaTitleBarHeightHintCore = 0;
        /// <summary>
        /// Gets or sets the extend client area title bar height hint.
        /// </summary>
        /// <value>The extend client area title bar height hint.</value>
        public int ExtendClientAreaTitleBarHeightHint
        {
            get => ExtendClientAreaTitleBarHeightHintCore;
            set => this.RaiseAndSetIfChanged(ref ExtendClientAreaTitleBarHeightHintCore, value);
        }

        /// <summary>
        /// The extend client area chrome hints core
        /// </summary>
        ExtendClientAreaChromeHints ExtendClientAreaChromeHintsCore = ExtendClientAreaChromeHints.Default;

        /// <summary>
        /// Gets or sets the extend client area chrome hints.
        /// </summary>
        /// <value>The extend client area chrome hints.</value>
        public ExtendClientAreaChromeHints ExtendClientAreaChromeHints
        {
            get => ExtendClientAreaChromeHintsCore;
            set => this.RaiseAndSetIfChanged(ref ExtendClientAreaChromeHintsCore, value);
        }

        /// <summary>
        /// Gets a value indicating whether this instance is windows style.
        /// </summary>
        /// <value><c>true</c> if this instance is windows style; otherwise, <c>false</c>.</value>
        [DependsOnProperty(nameof(Style))]
        public bool IsWindowsStyle => Style == ModernStyleType.WindowsMetro || (Style == ModernStyleType.Default && OperatingSystem.IsWindows());
        /// <summary>
        /// Gets a value indicating whether this instance is mac os style.
        /// </summary>
        /// <value><c>true</c> if this instance is mac os style; otherwise, <c>false</c>.</value>
        [DependsOnProperty(nameof(Style))]
        public bool IsMacOSStyle => Style == ModernStyleType.MacOS || (Style == ModernStyleType.Default && OperatingSystem.IsMacOS());

        /// <summary>
        /// Gets a value indicating whether this instance is classic style.
        /// </summary>
        /// <value><c>true</c> if this instance is classic style; otherwise, <c>false</c>.</value>
        [DependsOnProperty(nameof(Style))]
        public bool IsClassicStyle => Style == ModernStyleType.Classic || (Style == ModernStyleType.Default && OperatingSystem.IsLinux());

        /// <summary>
        /// Gets a value indicating whether this instance is modern style.
        /// </summary>
        /// <value><c>true</c> if this instance is modern style; otherwise, <c>false</c>.</value>
        [DependsOnProperty(nameof(Style))]
        public bool IsModernStyle => IsWindowsStyle || IsMacOSStyle;

        ModernStyleType StyleCore = ModernStyleType.Default;

        /// <summary>
        /// Gets or sets the style.
        /// </summary>
        /// <value>The style.</value>
        public ModernStyleType Style
        {
            get => StyleCore;
            set => this.RaiseAndSetIfChanged(ref StyleCore, value);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ModernStyleDataModel"/> class.
        /// </summary>
        public ModernStyleDataModel()
        {
            StyleCore = ApplicationSettings.Default.ModernStyle;

            this.PropertyChanged += OnPropertyChanged;

            // force reset values
            RaisePropertyChanged(nameof(Style));

            ApplicationSettings.Default.PropertyChanged += OnApplicationSettingChanged;
        }

        private void OnApplicationSettingChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(ApplicationSettings.Default.ModernStyle))
            {
                this.Style = ApplicationSettings.Default.ModernStyle;
            }
        }

        private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == nameof(Style))
            {
                if (IsMacOSStyle || IsWindowsStyle)
                {
                    ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.NoChrome;
                    ExtendClientAreaTitleBarHeightHint = -1;
                    ExtendClientAreaToDecorationsHint = true;
                }
                else
                {
                    ExtendClientAreaChromeHints = ExtendClientAreaChromeHints.Default;
                    ExtendClientAreaTitleBarHeightHint = 0;
                    ExtendClientAreaToDecorationsHint = false;
                }
            }
        }
    }
    public class ApplicationSettings : ReactiveObject
    {
        public static readonly ApplicationSettings Default = new ApplicationSettings();

        ModernStyleType _ModernStyle = ModernStyleType.Default;
        public ModernStyleType ModernStyle
        {
            get => _ModernStyle;
            set => this.RaiseAndSetIfChanged(ref _ModernStyle, value);
        }

        public void Save()
        {
            // todo...
        }
    }
    #endregion


}
<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="using:AvaloniaVisualStudioTitleBar.ViewModels"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
        x:Class="AvaloniaVisualStudioTitleBar.Views.MainWindow"
        Icon="/Assets/avalonia-logo.ico"
        Title="{Binding AppName}"
        ExtendClientAreaToDecorationsHint="{Binding ModernStyle.ExtendClientAreaToDecorationsHint}"
        ExtendClientAreaTitleBarHeightHint="{Binding ModernStyle.ExtendClientAreaTitleBarHeightHint}"
        ExtendClientAreaChromeHints="{Binding ModernStyle.ExtendClientAreaChromeHints}"
        >
    <Grid RowDefinitions="Auto,*" ColumnDefinitions="1*,Auto,1*">
        <Grid ColumnDefinitions="Auto, Auto, *,Auto">
            <Image Grid.Column="0" IsVisible="{Binding ModernStyle.IsWindowsStyle}" Source="/Assets/avalonia-logo.ico" Width="24" Height="24" Margin="4" VerticalAlignment="Center" HorizontalAlignment="Center" DoubleTapped="CloseWindow"></Image>
            <StackPanel
                Name="macButtonsStackPanel"
                Orientation="Horizontal"
                DockPanel.Dock="Left"
                Grid.Column="0"
                Spacing="6"
                Margin="6,0,0,0"
                IsVisible="{Binding ModernStyle.IsMacOSStyle}"
                Background="Transparent">
                <StackPanel.Styles>
                    <Style Selector="StackPanel:pointerover Path">
                        <Setter Property="IsVisible" Value="true"></Setter>
                    </Style>
                    <Style Selector="StackPanel:not(:pointerover) Path">
                        <Setter Property="IsVisible" Value="false"></Setter>
                    </Style>
                </StackPanel.Styles>
                <Button Name="macCloseButton"
                        HorizontalContentAlignment="Center"
                        VerticalContentAlignment="Center"
                        VerticalAlignment="Center"
                        Click="CloseWindow2"
                        Width="16"
                        Height="16">
                    <Button.Resources>
                        <CornerRadius x:Key="ControlCornerRadius">12</CornerRadius>
                    </Button.Resources>
                    <Button.Styles>
                        <Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
                            <Setter Property="Background" Value="#99FF5D55"/>
                        </Style>
                        <Style Selector="Button:not(:pointerover) /template/ ContentPresenter#PART_ContentPresenter">
                            <Setter Property="Background" Value="#FF5D55"/>
                        </Style>
                    </Button.Styles>

                    <Path Data="M 0,0 l 10,10 M 0,10 l 10,-10"
                          Stroke="#4C0102"
                          StrokeThickness="1"
                          Width="10"
                          Height="10"></Path>
                </Button>

                <Button Name="macMinimizeButton"
                        HorizontalContentAlignment="Center"
                        VerticalContentAlignment="Center"
                        VerticalAlignment="Center"
                        Click="MinimizeWindow"
                        Width="16"
                        Height="16">
                    <Button.Resources>
                        <CornerRadius x:Key="ControlCornerRadius">12</CornerRadius>
                    </Button.Resources>
                    <Button.Styles>
                        <Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
                            <Setter Property="Background" Value="#99FFBC2E"/>
                        </Style>
                        <Style Selector="Button:not(:pointerover) /template/ ContentPresenter#PART_ContentPresenter">
                            <Setter Property="Background" Value="#FFBC2E"/>
                        </Style>
                    </Button.Styles>

                    <Path Data="M 0,0 l 12,0"
                          Stroke="#985712"
                          StrokeThickness="1"
                          Width="12"
                          Height="1"></Path>
                </Button>

                <Button Name="macZoomButton"
                        HorizontalContentAlignment="Center"
                        VerticalContentAlignment="Center"
                        VerticalAlignment="Center"
                        Click="MaximizeWindow"
                        Width="16"
                        Height="16">
                    <Button.Resources>
                        <CornerRadius x:Key="ControlCornerRadius">12</CornerRadius>
                    </Button.Resources>
                    <Button.Styles>
                        <Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
                            <Setter Property="Background" Value="#9928C83E"/>
                        </Style>
                        <Style Selector="Button:not(:pointerover) /template/ ContentPresenter#PART_ContentPresenter">
                            <Setter Property="Background" Value="#28C83E"/>
                        </Style>
                    </Button.Styles>

                    <Path Data="M 0,10 l 8,0 l -8,-8 l 0,8 M 10,0 l 0,8 l -8,-8 l 8,0"
                          Fill="#0A630C"
                          StrokeThickness="0"
                          Width="10"
                          Height="10"></Path>
                </Button>
            </StackPanel>

            <Menu Name="sharedMainMenu" Grid.Column="1">
                <MenuItem Header="File">
                    <MenuItem Header="New"></MenuItem>
                    <Separator/>
                    <MenuItem Header="Open"></MenuItem>                    
                </MenuItem>    
            </Menu>

            <Panel Grid.Column="2" IsVisible="{Binding ModernStyle.IsMacOSStyle}" IsHitTestVisible="False"></Panel>
            <Image Grid.Column="3" IsVisible="{Binding ModernStyle.IsMacOSStyle}" IsHitTestVisible="False" Source="/Assets/avalonia-logo.ico" Width="24" Height="24" Margin="4" VerticalAlignment="Center" HorizontalAlignment="Right"></Image>
        </Grid>

        <StackPanel Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Center" Orientation="Horizontal">
            <TextBlock Text="{Binding AppName}" IsVisible="{Binding !ModernStyle.IsClassicStyle}" IsHitTestVisible="False" VerticalAlignment="Center"></TextBlock>
        </StackPanel>

        <StackPanel IsVisible="{Binding ModernStyle.IsWindowsStyle}"
                    HorizontalAlignment="Right"
                    Orientation="Horizontal"
                    Spacing="0"
                    Grid.Column="2"
                    >
            <Button Width="46"
                    Height="30"
                    HorizontalContentAlignment="Center"
                    VerticalContentAlignment="Center"
                    BorderThickness="0"
                    Name="winMinimizeButton"
                    Click="MinimizeWindow"
                    ToolTip.Tip="Minimize">
                <Button.Resources>
                    <CornerRadius x:Key="ControlCornerRadius">0</CornerRadius>
                </Button.Resources>
                <Button.Styles>
                    <Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
                        <Setter Property="Background" Value="#44AAAAAA"/>
                    </Style>
                    <Style Selector="Button:not(:pointerover) /template/ ContentPresenter#PART_ContentPresenter">
                        <Setter Property="Background" Value="Transparent"/>
                    </Style>
                </Button.Styles>
                <Path Margin="10,0,10,0"
                      Stretch="Uniform"
                      Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
                      Data="M2048 1229v-205h-2048v205h2048z"></Path>
            </Button>

            <Button Width="46"
                    VerticalAlignment="Stretch"
                    VerticalContentAlignment="Center"
                    BorderThickness="0"
                    Click="MaximizeWindow"
                    Name="winMaximizeButton">
                <ToolTip.Tip>
                    <ToolTip Content="Maximize"
                             Name="MaximizeToolTip"></ToolTip>
                </ToolTip.Tip>

                <Button.Resources>
                    <CornerRadius x:Key="ControlCornerRadius">0</CornerRadius>
                </Button.Resources>
                <Button.Styles>
                    <Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
                        <Setter Property="Background" Value="#44AAAAAA"/>
                    </Style>
                    <Style Selector="Button:not(:pointerover) /template/ ContentPresenter#PART_ContentPresenter">
                        <Setter Property="Background" Value="Transparent"/>
                    </Style>
                </Button.Styles>
                <Path Margin="10,0,10,0"
                      Stretch="Uniform"
                      Fill="{DynamicResource SystemControlForegroundBaseHighBrush}"
                      Name="winMaximizeIcon"
                      Data="M2048 2048v-2048h-2048v2048h2048zM1843 1843h-1638v-1638h1638v1638z"></Path>
            </Button>

            <Button Width="46"
                    VerticalAlignment="Stretch"
                    VerticalContentAlignment="Center"
                    BorderThickness="0"
                    Name="winCloseButton"
                    Click="CloseWindow2"
                    Grid.Column="0"
                    ToolTip.Tip="Close">
                <Button.Resources>
                    <CornerRadius x:Key="ControlCornerRadius">0</CornerRadius>
                </Button.Resources>
                <Button.Styles>
                    <Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
                        <Setter Property="Background" Value="Red"/>
                    </Style>
                    <Style Selector="Button:not(:pointerover) /template/ ContentPresenter#PART_ContentPresenter">
                        <Setter Property="Background" Value="Transparent"/>
                    </Style>
                    <Style Selector="Button:pointerover > Path">
                        <Setter Property="Fill" Value="White"/>
                    </Style>
                    <Style Selector="Button:not(:pointerover) > Path">
                        <Setter Property="Fill" Value="{DynamicResource SystemControlForegroundBaseHighBrush}"/>
                    </Style>
                </Button.Styles>
                <Path Margin="10,0,10,0"
                      Stretch="Uniform"
                      Data="M1169 1024l879 -879l-145 -145l-879 879l-879 -879l-145 145l879 879l-879 879l145 145l879 -879l879 879l145 -145z"></Path>
            </Button>
        </StackPanel>

        <ComboBox Grid.Row="1" Grid.Column="1" Width="250" ItemsSource="{Binding Styles}" SelectedItem="{Binding Style}">
            
        </ComboBox>
    </Grid>

</Window>

 

 

完整代码请参考github仓库:https://github.com/bodong1987/AvaloniaSamples/tree/main/AvaloniaVisualStudioTitleBar

 

标签:Style,Default,标题栏,System,value,Visual,Studio,public,ModernStyleType
From: https://www.cnblogs.com/bodong/p/18140056

相关文章

  • masscan下载编译安装,Visual Studio 2022
    Windowswin11编译masscan.exe,2024解决错误:LNK2019无法解析的外部符号e_next_bytee_next_int32第一步:克隆仓库https://github.com/robertdavidgraham/masscan.git第二部:VisualStudio打开vs10/masscan.sln第三步:项目-重定目标解决方案第四步:生成-重新生成解决方案......
  • Visual Studio 工具箱报错:未能加载工具箱项,将从项目中移除(转)
    未能加载工具箱项1篇文章0订阅订阅专栏我是在使用AnyCpu模式下,出现上面的错误,因为我目标平台设置的是X64解决办法:只需要把【项目名称右键->属性->生成->目标平台】设置成AnyCpu,一开始我设置的X64,因为有些第三方Nuget包不允许在X86下运行,所以为了使程序在X64下运行,把目标平台......
  • Android Studio制作简单登录界面
    实现目标应用线性布局设计登录界面,要求点击输入学号时弹出数字键盘界面,点击输入密码时弹出字母键盘,出现的文字、数字、尺寸等全部在values文件夹下相应.xml文件中设置好,使用时直接引用。当用户名或密码为空,显示一个提示信息“用户名与密码不能为空!”,当用户名和密码匹配,显示“登录......
  • 【译】宣布在 Visual Studio 17.10 预览2中为 ARM64 架构提供 SSDT
    我们很高兴地宣布在ARM64中为VisualStudio推出SQLServerDataTools(SSDT)。这个增强是在令人兴奋的17.10预览版2中发布的。arm64上VisualStudio的SSDT版本为arm64上的VisualStudio添加了SQL开发功能。这个最新的开发带来了许多专为增强您在ARM64设备上......
  • 用Visual Studio编写自动化测试百度网站的程序
    摘要使用VisualStudio2022,基于Selenium.WebDriver创建项目,模拟用户登入网站,进一步下单和修改收货地址。VisualStudio新建解决方案引入Selenium.WebDriver编写自动访问百度搜索的代码因为搜索网站只有一个输入条件嘛。第一版代码网址改为百度百度的输入框input......
  • Visual Studio Code & Python教程3顶级扩展
    3简介扩展功能非常宝贵。它们有助于提高代码质量,加快开发工作。我们将介绍一些必备的通用扩展。3.1顶级扩展3.1.1Pylance微软的Pylance可以大大提高你的工作效率。Pylance是一款Python语言服务器,它增强了IntelliSense、语法高亮和大量其他功能,为Python开发人员带来了令人......
  • Python程序员Visual Studio Code指南5调试
    5调试当运行程序时终端输出错误时,可以参考编辑器中的"问题"面板来解决遇到的问题。不过,并非所有错误都会导致错误。可能出现的情况是,程序执行成功,但输出结果与预期不同。出现这种情况时,下一步就是找出程序中的错误。这个过程被称为调试。您可以尝试通过注释代码行(从而禁止代码......
  • node笔记1:vue+node+mongodb+studio 3T创建登录模块
    1.创建node项目:expressnodenpmipackage.json修改如下代码,便于每次修改代码都可以刷新页面:"scripts":{"start":"node-dev./bin/www"}2.如果配合node设置反向代理;3.添加mongoose模块提供数据库信息:npmimongoose--save4.以登录功能模块为例,项目文件如下:model......
  • Visual Components对重型机械工业的影响 衡祖仿真
    一、重型机械行业面临的挑战此行业制造商面临着许多挑战,首先是世界各地实施的环境法规不断增多,可持续建筑实践、改善空气质量和减少排放已成为产品设计和开发背后的主要驱动力,健康和安全标准也在不断发展,给已经面临熟练劳动力短缺问题的制造商带来了新的负担。二、为什么使用Vi......
  • VS studio上查看标准cout输出
    VSstudio上查看标准cout输出网上的方法在解决方案管理器中,单击选中项目后,点击菜单【视图】->【属性页】在生成事件->生成后事件->命令行(BuildEvents->Post-BuildEvent->Command)Line)中增加$(OutDir)$(ProjectName).exe顾名思义,这个方法是在生成结束后,使用命令行执行生成的......