经过多年的Windows Forms开发,我决定尝试使用WPF。在我工作的公司中,我已经构建了大量基于MDI风格的软件,我希望在使用WPF时继续这样做。
我知道MDI并没有“支持”我的WPF,我正试图找到解决这个问题的方法。我也知道我可以通过WPF选项卡控件模仿MDI行为,但这对我来说不是最佳解决方案。
此处的用户习惯于拥有带有MDI表单的软件,其下有许多表单,用于异步监视不同的任务,并始终可见。
有没有什么方法可以在没有Tab或使用第三方控件的情况下在WPF中实现此功能,而且没有WinForms互操作的某些功能?
答案 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);
}
}
}