WPF and MDI model

本文关键字:model MDI and WPF | 更新日期: 2023-09-27 18:15:20

经过多年的Windows窗体开发,我决定尝试使用WPF。在我工作的公司,我已经建立了大量基于MDI风格的软件,我想在使用WPF时继续这样做。

我知道MDI不"支持"我的WPF,我正试图找到一个工作围绕这个问题。我也知道我可以通过WPF选项卡控件模拟MDI行为,但这对我来说不是最佳解决方案。

这里的用户习惯于使用带有MDI表单和下面的许多表单的软件,这些表单用于异步监视不同的任务,并且始终是可见的。

有没有办法在WPF中实现这个功能,而不使用Tab或使用第三方控件,而不使用某种WinForms互操作?

WPF and MDI model

一个选择是使用以下项目:http://wpfmdi.codeplex.com/

另一个选择是自己做一些东西,这允许有关多监视器行为等有趣的东西。

我使用弹出控件的功能为WPF构建了MDI。我建议您不要使用基于单独控件的常见win表单方式,而是使用MVVM用于WPF。因此,每个MDI窗口都是Popup,它包装了一个视图模型。应用程序本身是承载视图模型的基本窗口,并通过视图模型与窗口进行操作。

我提供了一个真实的MDI窗口示例。请注意,这不是完整的示例,但足以演示一些基本概念。所有必需的类都是自定义的,所以不依赖于第三方组件:

<DataTemplate DataType="{x:Type vm:Pane}">
    <DataTemplate.Resources>
        <ControlTemplate x:Key="uiFreePaneTemplate" TargetType="ContentControl">
            <Popup  
                x:Name="PART_DRAG" HorizontalOffset="{Binding X}" VerticalOffset="{Binding Y}"
                IsOpen="{Binding IsOpened}" VerticalAlignment="Top" HorizontalAlignment="Left"
                AllowsTransparency="True">
                <Border 
                    Width="{Binding Width}" Height="{Binding Height}" 
                    BorderThickness="2,2,2,2" Margin="0" CornerRadius="5" 
                    BorderBrush="{StaticResource WindowBackgroundBrush}"
                    Background="{StaticResource WindowBackgroundBrush}">
                    <ContentControl Template="{StaticResource Resizer}">
                        <Grid>
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto" />
                                <RowDefinition Height="*" />
                            </Grid.RowDefinitions>
                            <!--Pane header-->
                            <Thumb  Grid.Row="0" Width="Auto" Height="21">
                                <i:Interaction.Triggers>
                                    <i:EventTrigger EventName="DragDelta">
                                        <mvvm:EventToCommand Command="{Binding DragCommand}" PassEventArgsToCommand="True" />
                                    </i:EventTrigger>
                                </i:Interaction.Triggers>
                                <Thumb.Template>
                                    <ControlTemplate>
                                        <Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
                                            <Grid.Background>
                                                <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                                                    <GradientStop Color="#FF0C286C" Offset="1"/>
                                                    <GradientStop Color="Transparent"/>
                                                </LinearGradientBrush>
                                            </Grid.Background>
                                            <Grid.ColumnDefinitions>
                                                <ColumnDefinition Width="*"/>
                                                <ColumnDefinition Width="Auto"/>
                                            </Grid.ColumnDefinitions>
                                            <Label Content="{Binding Name}" Grid.Column="0" />
                                            <Button Template="{StaticResource CloseButton}" 
                                                Grid.Column="1"
                                                Margin="1,1,3,1" />
                                        </Grid>
                                    </ControlTemplate>
                                </Thumb.Template>
                            </Thumb>
                            <Grid Grid.Row="1" Background="{StaticResource ControlBackgroundBrush}">
                                <!--Pane content-->
                                <AdornerDecorator>
                                    <ContentPresenter Content="{TemplateBinding Content}" />
                                </AdornerDecorator>
                                <ResizeGrip x:Name="WindowResizeGrip" 
                                    HorizontalAlignment="Right" 
                                    VerticalAlignment="Bottom"
                                    IsTabStop="false"/>
                            </Grid>
                        </Grid>
                    </ContentControl>
                </Border>
            </Popup>
        </ControlTemplate>
    </DataTemplate.Resources>
    <ContentControl x:Name="uiBorder" Content="{Binding Model}" />
    <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding IsFree}" Value="True">
            <Setter TargetName="uiBorder" Property="ContentControl.Template" Value="{StaticResource uiFreePaneTemplate}" />
        </DataTrigger>
    </DataTemplate.Triggers>        
</DataTemplate>

视图模型:

public class Pane : HideableChildViewModel, IPane
{
    private IViewModel _model;
    private bool _isFree = true;
    private string _name;
    private double _coordinateX;
    private double _coordinateY;
    private double _width = 200.0;
    private double _height = 400.0;
    private ICommand _closeCommand;
    private ICommand _dragCommand;
    private ICommand _resizeCommand;
    /// <summary>
    /// Initializes a new instance of the Pane class.
    /// </summary>
    /// <param name="parent">The parent view model</param>
    /// <param name="parentPropertySelector">Selector of the parent property</param>
    /// <param name="model">VM to place within the pane</param>
    public Pane(
        IViewModel parent, 
        Expression<Func<object>> parentPropertySelector,
        IViewModel model)
        : base(parent, parentPropertySelector)
    {
        this.Model = model;
        this._dragCommand = new DragPaneCommand();
        this._resizeCommand = new ResizeCommand();
        if (model != null && model is ICloseableVM)
        {
            this._closeCommand = new ClosePaneCommand();
        }
        else
        {
            this._closeCommand = new HideCommand();
        }
    }
    #region Properties
    /// <summary>
    /// Gets or sets VM to place within the pane
    /// </summary>
    public IViewModel Model
    {
        get 
        {
            return this._model; 
        }
        set 
        {
            if (this._model != value)
            {
                this._model = value;
                this.RaisePropertyChanged(() => this.Model);
            }
        }
    }
    /// <summary>
    /// Gets or sets name of the pane
    /// </summary>
    [LayoutSettings(IsKey = true)]
    public string Name
    {
        get 
        {
            return this._name; 
        }
        set 
        {
            if (this._name != value)
            {
                this._name = value;
                this.RaisePropertyChanged(() => this.Name);
            }
        }
    }
    /// <summary>
    /// Gets or sets X coordinate
    /// </summary>
    [LayoutSettings]
    public double X
    {
        get 
        {
            return this._coordinateX; 
        }
        set 
        {
            if (this._coordinateX != value)
            {
                this._coordinateX = value;
                this.RaisePropertyChanged(() => this.X);
            }
        }
    }
    /// <summary>
    /// Gets or sets Y coordinate
    /// </summary>
    [LayoutSettings]
    public double Y
    {
        get 
        {
            return this._coordinateY; 
        }
        set 
        {
            if (this._coordinateY != value)
            {
                this._coordinateY = value;
                this.RaisePropertyChanged(() => this.Y);
            }
        }
    }
    /// <summary>
    /// Gets or sets width
    /// </summary>
    [LayoutSettings]
    public double Width
    {
        get
        {
            return this._width;
        }
        set
        {
            if (this._width != value)
            {
                this._width = value;
                this.RaisePropertyChanged(() => this.Width);
            }
        }
    }
    /// <summary>
    /// Gets or sets height
    /// </summary>
    [LayoutSettings]
    public double Height
    {
        get
        {
            return this._height;
        }
        set
        {
            if (this._height != value)
            {
                this._height = value;
                this.RaisePropertyChanged(() => this.Height);
            }
        }
    }
    /// <summary>
    /// Gets or sets a value indicating whether pane is free
    /// </summary>
    public bool IsFree
    {
        get
        {
            return this._isFree;
        }
        set
        {
            if (this._isFree != value)
            {
                this._isFree = value;
                this.OnIsFreeChanged(this._isFree);
                this.RaisePropertyChanged(() => this.IsFree);
            }
        }
    }
    #endregion
    #region Commands
    /// <summary>
    /// Gets command for pane closing
    /// </summary>
    public ICommand CloseCommand
    {
        get { return this._closeCommand; }
    }
    /// <summary>
    /// Gets command for pane dragging
    /// </summary>
    public ICommand DragCommand
    {
        get { return this._dragCommand; }
    }
    /// <summary>
    /// Gets command for pane resize
    /// </summary>
    public ICommand ResizeCommand
    {
        get { return this._resizeCommand; }
    }
    #endregion
    private void OnIsFreeChanged(bool isFree)
    {
        if (!isFree)
        {
            return;
        }
        IDockContainer oContainer = ((IChildViewModel)((IChildViewModel)this.Parent).Parent).Parent as IDockContainer;
        if (oContainer != null)
        {
            this.SetParent(oContainer, () => oContainer.FreeItems);
        }
    }
}