WPF和MDI模型

时间:2011-11-21 08:53:24

标签: c# wpf

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

我知道MDI并没有“支持”我的WPF,我正试图找到解决这个问题的方法。我也知道我可以通过WPF选项卡控件模仿MDI行为,但这对我来说不是最佳解决方案。

此处的用户习惯于拥有带有MDI表单的软件,其下有许多表单,用于异步监视不同的任务,并始终可见。

有没有什么方法可以在没有Tab或使用第三方控件的情况下在WPF中实现此功能,而且没有WinForms互操作的某些功能?

2 个答案:

答案 0 :(得分:0)

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

其他选择是自己制作一些东西,这允许有关多监视器行为的有趣内容等。

答案 1 :(得分:0)

我使用Popup控件的功能为WPF构建了MDI。我建议你不要使用基于单独控件的常见win表单方式,而是使用MVVM for 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);
        }
    }
}