You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF:创建多视图复用控件的最优方案——通用网格结构复用需求

嘿,这个需求太常见啦!在WPF里复用这种固定的三行Grid布局,有几个非常靠谱的方案,我给你拆解一下,你可以根据自己的项目场景选最合适的:

方案1:封装自定义UserControl(最推荐)

这是最直观、上手最快的方式,把重复的Grid结构打包成一个可复用的UserControl,再通过依赖属性暴露三个区域的内容入口,让各个页面可以灵活填充不同内容。

步骤1:创建UserControl的XAML结构

新建一个名为ThreeRowGridContainer的UserControl,写入你的Grid布局,并给三个区域添加ContentPresenter作为内容容器:

<UserControl x:Class="YourProjectNamespace.ThreeRowGridContainer"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        
        <!-- 顶部内容容器 -->
        <ContentPresenter Grid.Row="0" 
                          Content="{Binding TopContent, RelativeSource={RelativeSource AncestorType=UserControl}}" />
        
        <!-- 中间内容容器(占满剩余空间) -->
        <ContentPresenter Grid.Row="1" 
                          Content="{Binding MiddleContent, RelativeSource={RelativeSource AncestorType=UserControl}}" />
        
        <!-- 底部内容容器 -->
        <ContentPresenter Grid.Row="2" 
                          Content="{Binding BottomContent, RelativeSource={RelativeSource AncestorType=UserControl}}" />
    </Grid>
</UserControl>

步骤2:添加依赖属性

在UserControl的后台代码中,定义三个依赖属性,让外部可以设置对应区域的内容:

using System.Windows;
using System.Windows.Controls;

namespace YourProjectNamespace
{
    public partial class ThreeRowGridContainer : UserControl
    {
        public ThreeRowGridContainer()
        {
            InitializeComponent();
        }

        // 顶部内容依赖属性
        public static readonly DependencyProperty TopContentProperty =
            DependencyProperty.Register(nameof(TopContent), typeof(object), typeof(ThreeRowGridContainer));

        public object TopContent
        {
            get => GetValue(TopContentProperty);
            set => SetValue(TopContentProperty, value);
        }

        // 中间内容依赖属性
        public static readonly DependencyProperty MiddleContentProperty =
            DependencyProperty.Register(nameof(MiddleContent), typeof(object), typeof(ThreeRowGridContainer));

        public object MiddleContent
        {
            get => GetValue(MiddleContentProperty);
            set => SetValue(MiddleContentProperty, value);
        }

        // 底部内容依赖属性
        public static readonly DependencyProperty BottomContentProperty =
            DependencyProperty.Register(nameof(BottomContent), typeof(object), typeof(ThreeRowGridContainer));

        public object BottomContent
        {
            get => GetValue(BottomContentProperty);
            set => SetValue(BottomContentProperty, value);
        }
    }
}

步骤3:在页面中使用

现在你可以在任意页面里直接引用这个控件,填充自定义内容:

<Window xmlns:local="clr-namespace:YourProjectNamespace">
    <local:ThreeRowGridContainer>
        <local:ThreeRowGridContainer.TopContent>
            <!-- 这里放顶部内容,比如导航栏 -->
            <StackPanel Orientation="Horizontal" Padding="10">
                <Button Content="首页" Margin="0 0 10 0"/>
                <Button Content="设置"/>
            </StackPanel>
        </local:ThreeRowGridContainer.TopContent>
        
        <local:ThreeRowGridContainer.MiddleContent>
            <!-- 这里放页面主内容,比如数据表格或文本 -->
            <TextBlock Text="这是页面的核心内容区域" 
                       HorizontalAlignment="Center" 
                       VerticalAlignment="Center"
                       FontSize="18"/>
        </local:ThreeRowGridContainer.MiddleContent>
        
        <local:ThreeRowGridContainer.BottomContent>
            <!-- 这里放底部内容,比如版权信息 -->
            <TextBlock Text="© 2024 你的项目名称" 
                       HorizontalAlignment="Center" 
                       Padding="10"/>
        </local:ThreeRowGridContainer.BottomContent>
    </local:ThreeRowGridContainer>
</Window>
方案2:使用ControlTemplate(样式化场景)

如果你的项目需要支持换肤,或者希望布局能应用到不同控件上,ControlTemplate是更灵活的选择。你可以给ContentControl定义一个模板,把三行Grid结构嵌入其中:

<Window.Resources>
    <Style x:Key="ThreeRowGridTemplateStyle" TargetType="ContentControl">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="ContentControl">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*" />
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition Height="*" />
                            <RowDefinition Height="Auto" />
                        </Grid.RowDefinitions>
                        
                        <!-- 顶部区域:可通过附加属性或自定义属性绑定 -->
                        <ContentPresenter Grid.Row="0" 
                                          Content="{Binding TopContent, RelativeSource={RelativeSource TemplatedParent}}"/>
                        <!-- 中间区域:绑定ContentControl的默认Content -->
                        <ContentPresenter Grid.Row="1" 
                                          Content="{TemplateBinding Content}"/>
                        <!-- 底部区域:同样通过自定义属性绑定 -->
                        <ContentPresenter Grid.Row="2" 
                                          Content="{Binding BottomContent, RelativeSource={RelativeSource TemplatedParent}}"/>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>

使用时需要给ContentControl添加对应的自定义属性(和UserControl的依赖属性逻辑类似),适合需要高度样式定制的场景。

方案3:DataTemplate + MVVM(架构解耦场景)

如果你的项目采用MVVM架构,可以通过定义基类ViewModel和对应的DataTemplate来实现布局复用:

步骤1:定义基类ViewModel

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class ThreeRowBaseViewModel : INotifyPropertyChanged
{
    private object _topContentVM;
    public object TopContentVM
    {
        get => _topContentVM;
        set { _topContentVM = value; OnPropertyChanged(); }
    }

    private object _middleContentVM;
    public object MiddleContentVM
    {
        get => _middleContentVM;
        set { _middleContentVM = value; OnPropertyChanged(); }
    }

    private object _bottomContentVM;
    public object BottomContentVM
    {
        get => _bottomContentVM;
        set { _bottomContentVM = value; OnPropertyChanged(); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

步骤2:定义DataTemplate

<Window.Resources>
    <DataTemplate DataType="{x:Type local:ThreeRowBaseViewModel}">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            
            <ContentControl Grid.Row="0" Content="{Binding TopContentVM}"/>
            <ContentControl Grid.Row="1" Content="{Binding MiddleContentVM}"/>
            <ContentControl Grid.Row="2" Content="{Binding BottomContentVM}"/>
        </Grid>
    </DataTemplate>
</Window.Resources>

步骤3:页面ViewModel继承基类

每个页面的ViewModel继承ThreeRowBaseViewModel,设置对应的子ViewModel,然后在View中用ContentControl绑定即可,实现布局和业务逻辑的完全解耦。

方案对比
方案适用场景优点缺点
自定义UserControl简单布局复用、非MVVM/轻MVVM上手快、代码直观、易维护样式定制灵活性稍弱
ControlTemplate需要换肤、多控件共享布局样式高度可定制需要额外处理属性绑定
DataTemplate+MVVM复杂MVVM架构项目完全解耦布局与业务逻辑学习成本较高、代码量略大

我个人最推荐自定义UserControl方案,对于你这种5个页面复用的场景来说,它的实现成本最低,代码最清晰,维护起来也最省心。

内容的提问来源于stack exchange,提问作者Werner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:24:42