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互操作?
一个选择是使用以下项目: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);
}
}
}