Wpf 基于子属性设置窗体最小宽度和最小高度

Wpf 基于子属性设置窗体最小宽度和最小高度,wpf,mvvm,Wpf,Mvvm,我正在使用MVVM模式在WPF中编写一个应用程序。在我的应用程序中,我有一个ipoppupWindowsService,我用它创建一个弹出对话框窗口 因此,要在弹出窗口中显示ViewModel,您可以执行以下操作: var container = ServiceLocator.Current.GetInstance<IUnityContainer>(); var popupService = container.Resolve<IPopupWindowService>()

我正在使用MVVM模式在WPF中编写一个应用程序。在我的应用程序中,我有一个
ipoppupWindowsService
,我用它创建一个弹出对话框窗口

因此,要在弹出窗口中显示ViewModel,您可以执行以下操作:

var container = ServiceLocator.Current.GetInstance<IUnityContainer>();
var popupService = container.Resolve<IPopupWindowService>();
var myViewModel = container.Resolve<IMyViewModel>();
popupService.Show((ViewModelBase)myViewModel);
<Window x:Class="TheCompany.Cubit.Shell.Views.PopupWindowView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        SizeToContent="WidthAndHeight"
        WindowStartupLocation="CenterOwner">
<DockPanel x:Name="panelContent">
    <StackPanel HorizontalAlignment="Right" DockPanel.Dock="Bottom" Orientation="Horizontal" Visibility="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=ButtonPanelVisibility}">
        <Button Width="75" IsDefault="True" x:Uid="ViewDialog_AcceptButton" Click="OnAcceptButtonClick" Margin="4">OK</Button>
        <Button Width="75" IsCancel="True" x:Uid="ViewDialog_CancelButton" Click="OnCancelButtonClick" Margin="0,4,4,4">Cancel</Button>
    </StackPanel>
    <ContentPresenter Content="{Binding}" />
</DockPanel>
我的弹出视图如下所示:

var container = ServiceLocator.Current.GetInstance<IUnityContainer>();
var popupService = container.Resolve<IPopupWindowService>();
var myViewModel = container.Resolve<IMyViewModel>();
popupService.Show((ViewModelBase)myViewModel);
<Window x:Class="TheCompany.Cubit.Shell.Views.PopupWindowView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        SizeToContent="WidthAndHeight"
        WindowStartupLocation="CenterOwner">
<DockPanel x:Name="panelContent">
    <StackPanel HorizontalAlignment="Right" DockPanel.Dock="Bottom" Orientation="Horizontal" Visibility="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=ButtonPanelVisibility}">
        <Button Width="75" IsDefault="True" x:Uid="ViewDialog_AcceptButton" Click="OnAcceptButtonClick" Margin="4">OK</Button>
        <Button Width="75" IsCancel="True" x:Uid="ViewDialog_CancelButton" Click="OnCancelButtonClick" Margin="0,4,4,4">Cancel</Button>
    </StackPanel>
    <ContentPresenter Content="{Binding}" />
</DockPanel>

好啊
取消

您可以在ViewModel上定义
MinHeight
MinWidth
属性,并使用数据绑定将视图绑定到XAML中的这些属性:

<...
    MinHeight="{Binding Path=MinHeight}"
    MinWidth="{Binding Path=MinWidth}"
.../>

您可以在ViewModel上定义
MinHeight
MinWidth
属性,并使用数据绑定将视图绑定到XAML中的这些属性:

<...
    MinHeight="{Binding Path=MinHeight}"
    MinWidth="{Binding Path=MinWidth}"
.../>

我设计了完全相同的通用模式对话框控件(使用针对类型的数据模板),也遇到了这个问题

  • 使用RelativeSource不起作用,因为您只能通过这种方式找到祖先(afaik)

  • 另一种可能的解决方案是命名ContentPresenter,并使用ElementName绑定绑定到其上的属性。但是,ContentPresenter不会从其呈现的可视子级“继承”MinHeight和MinWidth属性

最终的解决方案是使用VisualTreeHelper在运行时获取与ViewModel关联的已解析视图:

        DependencyObject lObj = VisualTreeHelper.GetChild(this.WindowContent, 0);
        if (lObj != null && lObj is FrameworkElement)
        {
            lWindowContentMinHeight = ((FrameworkElement)lObj).MinHeight;
            lWindowContentMinWidth = ((FrameworkElement)lObj).MinWidth;
        }
我将这段代码放在ModalDialogView的代码背后的OnActivated()覆盖中(在OnInitialized中,视图还不能被解析)

唯一剩下的问题是修正这些最小值,以便将窗口宽度和按钮面板高度考虑在内

更新

下面是我在通用模态对话框中使用的代码。它解决了以下附加问题:

  • 它正确地以所有者窗口为中心
  • 如果内容未设置MinWidth和MinHeight,则它不会执行任何操作

    private bool _MinSizeSet = false;
    
    public ModalDialogView(object pDataContext)
        : this()
    {
        this.DataContext = pDataContext;
        this.LayoutUpdated += new EventHandler(ModalDialogView_LayoutUpdated);
    }
    
    void ModalDialogView_LayoutUpdated(object sender, EventArgs e)
    {
        if (System.Windows.Media.VisualTreeHelper.GetChildrenCount(this.WindowContent) > 0)
            SetInitialAndMinimumSize();
    }
    
    private void SetInitialAndMinimumSize()
    {
        FrameworkElement lElement = VisualTreeHelper.GetChild(this.WindowContent, 0) as FrameworkElement;
        if (!_MinSizeSet && lElement != null)
        {
            if (lElement.MinWidth != 0 && lElement.MinHeight != 0)
            {
                double lHeightDiff = this.ActualHeight - this.WindowContent.ActualHeight;
                double lWidthDiff = this.ActualWidth - this.WindowContent.ActualWidth;
                this.MinHeight = lElement.MinHeight + lHeightDiff;
                this.MinWidth = lElement.MinWidth + lWidthDiff;
                this.SizeToContent = SizeToContent.Manual;
                this.Height = this.MinHeight;
                this.Width = this.MinWidth;
                this.Left = this.Owner.Left + (this.Owner.ActualWidth - this.ActualWidth) / 2;
                this.Top = this.Owner.Top + (this.Owner.ActualHeight - this.ActualHeight) / 2;
            }
            _MinSizeSet = true;
        }
    }
    

我设计了完全相同的通用模式对话框控件(使用针对类型的数据模板),也遇到了这个问题

  • 使用RelativeSource不起作用,因为您只能通过这种方式找到祖先(afaik)

  • 另一种可能的解决方案是命名ContentPresenter,并使用ElementName绑定绑定到其上的属性。但是,ContentPresenter不会从其呈现的可视子级“继承”MinHeight和MinWidth属性

最终的解决方案是使用VisualTreeHelper在运行时获取与ViewModel关联的已解析视图:

        DependencyObject lObj = VisualTreeHelper.GetChild(this.WindowContent, 0);
        if (lObj != null && lObj is FrameworkElement)
        {
            lWindowContentMinHeight = ((FrameworkElement)lObj).MinHeight;
            lWindowContentMinWidth = ((FrameworkElement)lObj).MinWidth;
        }
我将这段代码放在ModalDialogView的代码背后的OnActivated()覆盖中(在OnInitialized中,视图还不能被解析)

唯一剩下的问题是修正这些最小值,以便将窗口宽度和按钮面板高度考虑在内

更新

下面是我在通用模态对话框中使用的代码。它解决了以下附加问题:

  • 它正确地以所有者窗口为中心
  • 如果内容未设置MinWidth和MinHeight,则它不会执行任何操作

    private bool _MinSizeSet = false;
    
    public ModalDialogView(object pDataContext)
        : this()
    {
        this.DataContext = pDataContext;
        this.LayoutUpdated += new EventHandler(ModalDialogView_LayoutUpdated);
    }
    
    void ModalDialogView_LayoutUpdated(object sender, EventArgs e)
    {
        if (System.Windows.Media.VisualTreeHelper.GetChildrenCount(this.WindowContent) > 0)
            SetInitialAndMinimumSize();
    }
    
    private void SetInitialAndMinimumSize()
    {
        FrameworkElement lElement = VisualTreeHelper.GetChild(this.WindowContent, 0) as FrameworkElement;
        if (!_MinSizeSet && lElement != null)
        {
            if (lElement.MinWidth != 0 && lElement.MinHeight != 0)
            {
                double lHeightDiff = this.ActualHeight - this.WindowContent.ActualHeight;
                double lWidthDiff = this.ActualWidth - this.WindowContent.ActualWidth;
                this.MinHeight = lElement.MinHeight + lHeightDiff;
                this.MinWidth = lElement.MinWidth + lWidthDiff;
                this.SizeToContent = SizeToContent.Manual;
                this.Height = this.MinHeight;
                this.Width = this.MinWidth;
                this.Left = this.Owner.Left + (this.Owner.ActualWidth - this.ActualWidth) / 2;
                this.Top = this.Owner.Top + (this.Owner.ActualHeight - this.ActualHeight) / 2;
            }
            _MinSizeSet = true;
        }
    }
    

能否演示如何绑定View和ViewModel?特别是那些属性。您能演示一下如何绑定View和ViewModel吗?特别是那些属性。我考虑过这个选项,但我希望在我的ViewModel中不包含这种信息。但这可能是唯一的办法……我同意马克的看法。Jon,如果您不想将此信息放入视图模型中,或者您可以使用Settings类,并将此特定属性绑定到它…为什么要将其排除在视图模型之外?ViewModel是一个为视图建模的类。我不喜欢将大小放在ViewModel中的原因是它依赖于视图。我可能希望完全更改表单的布局,从而更改我希望的最小/最大大小。这意味着我必须更改ViewModel以适应视图中的更改。我甚至可能创建一个特定于SilverLight的版本,该版本具有不同的布局,因此具有不同的大小。希望您能看到我的来历。您不必更改ViewModel,只需更改这些属性的值。这正是ViewModels的优点之一:您可以将此类逻辑放入POCO类中,在该类中可以实现所需的简单或复杂逻辑。我考虑过该选项,但我希望将此类信息排除在ViewModel之外。但这可能是唯一的办法……我同意马克的看法。Jon,如果您不想将此信息放入视图模型中,或者您可以使用Settings类,并将此特定属性绑定到它…为什么要将其排除在视图模型之外?ViewModel是一个为视图建模的类。我不喜欢将大小放在ViewModel中的原因是它依赖于视图。我可能希望完全更改表单的布局,从而更改我希望的最小/最大大小。这意味着我必须更改ViewModel以适应视图中的更改。我甚至可能创建一个特定于SilverLight的版本,该版本具有不同的布局,因此具有不同的大小。希望您能看到我的来历。您不必更改ViewModel,只需更改这些属性的值。这正是ViewModels的优点之一:您可以将此类逻辑放入POCO类中,在该类中可以实现所需的简单或复杂逻辑。我想指出的是,我还认为这些视图特定的属性(MinHeight和MinWidth)不属于ViewModel。我想说明的是,我还认为这些视图特定属性(MinHeight和MinWidth)不属于ViewModel。