【问题标题】:WPF and MDI modelWPF 和 MDI 模型
【发布时间】:2011-11-21 08:53:23
【问题描述】:

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

我知道我的 WPF 不“支持”MDI,我正在努力寻找解决这个问题的方法。我也知道我可以通过 WPF 选项卡控件模拟 MDI 行为,但这对我来说不是最佳解决方案。

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

有没有什么方法可以在 WPF 中实现此功能而无需 Tab 或使用 3rd 方控件且无需一些 WinForms 互操作?

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

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

    其他选项是自己制作一些东西,这允许与多显示器行为等相关的有趣内容。

    【讨论】:

    • 感谢您的回复!我知道这个项目,但我宁愿不使用另一个控件/库来做到这一点。希望我能找到一种方法,只使用框架而不使用其他任何东西,即,希望有某种我不知道的 wpf- 技巧。
    【解决方案2】:

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

    【讨论】:

    • 嘿 Pavel,感谢您的(详细)回答。在我走这条路之前,我必须仔细研究你的解决方案。我知道它是一个“骨架”,与 WinForms 的非常简单的 MDI 模型相比,我觉得它有点矫枉过正,但我​​愿意试一试。我很快就会回来。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-17
    相关资源
    最近更新 更多