WPF 自定义窗体(一)
阅读原文时间:2023年09月05日阅读:1

.Net默认的窗体样式只有四种:None、SingleBorderWindow、ThreeDBorderWindow、ToolWindow,都比较“丑”。而很多时候,我们希望自定义窗体,比如,无边框,有阴影,或者有模糊效果等。

在WPF中,要实现自定义窗体比较简单,主要有两种方法:

1)使用WindowChrome;

2)使用WindowStyle = “None”。

一、使用WindowChrome。

  WindowChrome,可以翻译为:窗体装饰条,官方文档中的定义是:表示一个对象,它描述窗口非工作区区域的自定义。(官方链接:WindowChrome 类 (System.Windows.Shell) | Microsoft Learn%26rd%3Dtrue))

  在官方的解释中,窗口由两部分构成:客户区域,非客户区域。

图中,Client Area表示客户区域;其他的部分,统称为非客户区域。

那么WindowChrome的作用是,将客户区域扩展至整个窗体(遮住了非客户区),同时提供部分标准窗体的功能。如下所示:

<!-- WindowChrome将客户区域扩展至整个窗体,并遮住标题栏、按钮等-->  
<WindowChrome.WindowChrome>  
    <WindowChrome />  
</WindowChrome.WindowChrome>

<Grid>  
    <TabControl>  
        <TabItem Header="项目"/>  
        <TabItem Header="代码"/>  
    </TabControl>  
</Grid>  

   备注:这里的边框,是TabControl的边框,不是窗体的边框。

用上WindowChrome后,会惊奇的发现:在原标题栏的位置,可以用鼠标拖动了;在窗体的四周,可以调整窗体的大小了!Amazing!

但同时,又出现了一个新的问题:窗体中的所以内容,都不能交互(鼠标点击,用户输入)了。

这是为什么呢?可以这样理解。WindowChrome就像一个图层,它将窗体整个覆盖住了。因此窗体上的内容,自然就操作不了。那要如何才能点击呢?

这需要给交互控件,添加WindowChrome的附件属性:IsHitTestVisibleInChrome。如下所示。

<Grid>  
    <!-- 使用WindowChrome的附件属性 -->  
    <TabControl WindowChrome.IsHitTestVisibleInChrome="True">  
        <TabItem Header="项目"/>  
        <TabItem Header="代码"/>  
    </TabControl>  
</Grid>

  如果你以为这样就万事大吉了,那只能说太天真了,微软的东西,哪有那么简单的呢??哈哈~

  如果真的按照这个代码,你会发现,又不能使用鼠标拖动窗体了。这是为什么呢?明明之前都可以,为何为控件添加了一个附加属性后,就不行了呢?

  问题肯定出在WindowChrome上。那么我们再来看看WindowChrome:

图中有颜色的区域,实际上均为透明的,看不见的。此处附上颜色则是为了方便解释。

这个图就是WindowChrome的模型。其中Caption区域,表示标题栏,就是它,允许窗体被鼠标拖动。GlassFrameThickness就是Aero窗体的透明边框(Aero主体只在部分操作系统中支持)。ResizeBorderThickness就是调整窗体大小的边框的粗细,它提供了使用鼠标调整窗体大小的功能。而CornerRadius,则将窗体变成了圆角,它只有在GlassFrameThickness = 0 或者未启用Aero主体的窗口中才有效。。

再回到上面的问题,为什么添加了附加属性,就不能用鼠标拖动窗体了呢?

原因在于,TabControl进入了Caption区域。因为设置了附加属性(IsHitTestVisibleInChrome),表示鼠标可以“击穿”WindowChrome,那么自然就无法“点击”到Caption区域,自然就无法拖动窗体了。

那么如果解决这个问题呢?以及如何添加按钮呢?答案是手动添加标题栏。哈哈~ 如下代码所示:

Xaml代码:

<!-- WindowChrome将客户区域扩展至整个窗体,并遮住标题栏、按钮等 -->  
<WindowChrome.WindowChrome>  

     

<Border BorderThickness="1">  
    <Grid>  
        <Grid.RowDefinitions>  
            <RowDefinition Height="auto"/>  
            <RowDefinition Height="\*"/>  
        </Grid.RowDefinitions>  
        <Border Height="30" Background="YellowGreen">  
            <Grid>  
                <Grid.Resources>  
                    <Style TargetType="Button">  
                        <Setter Property="Width" Value="30"/>  
                        <Setter Property="Background" Value="Transparent"/>  
                        <Setter Property="BorderThickness" Value="0"/>  
                    </Style>  
                </Grid.Resources>  
                <StackPanel Orientation="Horizontal" WindowChrome.IsHitTestVisibleInChrome="True">  
                    <Image />  
                    <TextBlock VerticalAlignment="Center" Margin="3,0" Text="{Binding Title, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}}"/>  
                </StackPanel>

                <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" WindowChrome.IsHitTestVisibleInChrome="True">  
                    <Button Content="\_" Click="Btn\_Min"/>  
                    <Button Content="Max" Click="Btn\_Max"/>  
                    <Button Content="X" Click="Btn\_Close"/>  
                </StackPanel>  
            </Grid>  
        </Border>  
        <!-- 使用WindowChrome的附件属性 -->  
        <TabControl Grid.Row="1" WindowChrome.IsHitTestVisibleInChrome="True">  
            <TabItem Header="项目"/>  
            <TabItem Header="代码"/>  
        </TabControl>  
    </Grid>  
</Border>  

C# 代码:

public partial class WindowNone : Window
{
public WindowNone()
{
InitializeComponent();
}

// 最小化  
private void Btn\_Min(object sender, RoutedEventArgs e)  
{  
    this.WindowState = WindowState.Minimized;  
}  

  // 最大化、还原
private void Btn_Max(object sender, RoutedEventArgs e)
{
if(this.WindowState == WindowState.Normal)
{
this.WindowState = WindowState.Maximized;
}
else
{
this.WindowState = WindowState.Normal;
}
}

  // 关闭窗体
private void Btn_Close(object sender, RoutedEventArgs e)
{
this.Close();
}
}

 

手动添加了标题栏之后,在标题栏上,你就可以放上任何你放的东西。。。。

二、使用WindowStyle = "None"

将窗体的WindowStyle属性设置为None后,窗体呈现这样:

  

这里,你会发现,窗体可以通过鼠标调整大小,但是不能用鼠标拖动。那解决的办法是什么呢?同样是手动设置一个标题栏:

Xaml 代码:






            <StackPanel Orientation="Horizontal" HorizontalAlignment="Right" WindowChrome.IsHitTestVisibleInChrome="True">  
                <Button Content="\_" Click="Btn\_Min"/>  
                <Button Content="Max" Click="Btn\_Max"/>  
                <Button Content="X" Click="Btn\_Close"/>  
            </StackPanel>  
        </Grid>  
    </Border>  
    <TabControl Grid.Row="1" Margin="10">  
        <TabItem Header="项目"/>  
        <TabItem Header="代码"/>  
    </TabControl>  
</Grid>  

C# 代码:

public partial class NoneWindow : Window  
{  
    public NoneWindow()  
    {  
        InitializeComponent();  
    }

    // 窗体移动  
    private void TitleMove(object sender, MouseButtonEventArgs e)  
    {  
        if (e.ChangedButton != MouseButton.Left) return;            // 非左键点击,退出  
        if (e.ClickCount == 1)  
        {  
            this.DragMove();                                        // 拖动窗体  
        }  
        else  
        {  
            WindowMax();                                            // 双击时,最大化或者还原窗体  
        }  
    }

    // 最小化  
    private void Btn\_Min(object sender, RoutedEventArgs e)  
    {  
        this.WindowState = WindowState.Minimized;  
    }

    // 关闭窗体  
    private void Btn\_Close(object sender, RoutedEventArgs e)  
    {  
        this.Close();  
    }

    // 最大化、还原  
    private void Btn\_Max(object sender, RoutedEventArgs e)  
    {  
        WindowMax();  
    }

    private void WindowMax()  
    {  
        if (this.WindowState == WindowState.Normal)  
        {  
            this.WindowState = WindowState.Maximized;  
        }  
        else  
        {  
            this.WindowState = WindowState.Normal;  
        }  
    }

}

  

这种方式下,会发现在窗体的“标题栏”上面,还有一点留白无法去除,同样窗体的边框也是无法去除的。

如果解决?且听下回分解。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章