Wpf 创建方形用户控件

Wpf 创建方形用户控件,wpf,user-controls,grid,microsoft-metro,winrt-xaml,Wpf,User Controls,Grid,Microsoft Metro,Winrt Xaml,我正在设计一个需要方形的用户控件,它是一个棋盘,用来填充给定上下文的空间 我的用户控件看起来像: 现在我只想说,这个网格必须是正方形的,不管它有多大的空间需要扩展 徒劳地尝试解决方案: 我不能使用UniformGrid,因为实际上我还有行和列的名称,所以我有一个不同大小的前导标题行和列 如果我使用统一的Viewbox,它会把一切搞糟 我试着用经典的 ... 但它只有在手动设置Width属性时才起作用。否则,将忽略此约束 结论 我不知道,我真的想避免手动设置宽度/高度,因为这个控件可能会在许多不

我正在设计一个需要方形的用户控件,它是一个棋盘,用来填充给定上下文的空间

我的用户控件看起来像:

现在我只想说,这个网格必须是正方形的,不管它有多大的空间需要扩展

徒劳地尝试解决方案:

我不能使用UniformGrid,因为实际上我还有行和列的名称,所以我有一个不同大小的前导标题行和列

如果我使用统一的Viewbox,它会把一切搞糟

我试着用经典的

... 但它只有在手动设置Width属性时才起作用。否则,将忽略此约束

结论

我不知道,我真的想避免手动设置宽度/高度,因为这个控件可能会在许多不同的地方使用ListItem模板、游戏等

建议的解决方案:

有一个解决方案可以使用一些代码隐藏。我没有找到一个XAML唯一的解决方案

网格现在是:

... 事件处理程序是:

私有无效板\u FullControlSizeChangedobject发送方,SizeChangedEventArgs { double size=Math.min args.NewSize.Height,args.NewSize.Width; Gridsender.Width=大小; Gridsender.高度=大小; } 可以将Height属性绑定到ActualWidth而不是Width:

但是,更好的解决方案是使用Viewbox。避免其混乱的诀窍是通过定义宽度和高度的合理相等值使其子项成为正方形:

<Viewbox>
    <Grid Width="500" Height="500">
        ...              
    </Grid>
</Viewbox>
可以将Height属性绑定到ActualWidth而不是Width:

但是,更好的解决方案是使用Viewbox。避免其混乱的诀窍是通过定义宽度和高度的合理相等值使其子项成为正方形:

<Viewbox>
    <Grid Width="500" Height="500">
        ...              
    </Grid>
</Viewbox>

我最初尝试将您的绑定修改为ActualWidth,但当网格是顶级元素时,它仍然不起作用,在某些情况下,它最终将控件扩展到了超出可用大小的范围。因此,尝试了其他一些获得所需输出的方法

有两种方法可以解决这个问题:

由于这是一个与视图相关的问题,不破坏MVVM,保持正方形,如果您同意保留一点代码,您可以执行以下操作

private void OnSizeChanged(object sender, SizeChangedEventArgs e) {
  double minNewSizeOfParentUserControl = Math.Min(e.NewSize.Height, e.NewSize.Width);
  mainGrid.Width = minNewSizeOfParentUserControl;
  mainGrid.Height = minNewSizeOfParentUserControl;
}
在xaml中,您可以将主顶级网格命名为mainGrid,并将UserControl size changed事件处理程序附加到上述函数,而不是网格本身

然而,无论出于何种原因,如果您完全讨厌代码隐藏,那么您可以稍微花哨一点,创建一种行为,例如

public class GridSquareSizeBehavior : Behavior<Grid> {
  private UserControl _parent;

  protected override void OnAttached() {
    DependencyObject ucParent = AssociatedObject.Parent;
    while (!(ucParent is UserControl)) {
      ucParent = LogicalTreeHelper.GetParent(ucParent);
    }
    _parent = ucParent as UserControl;
    _parent.SizeChanged += SizeChangedHandler;
    base.OnAttached();
  }

  protected override void OnDetaching() {
    _parent.SizeChanged -= SizeChangedHandler;
    base.OnDetaching();
  }

  private void SizeChangedHandler(object sender, SizeChangedEventArgs e) {
    double minNewSizeOfParentUserControl = Math.Min(e.NewSize.Height, e.NewSize.Width);
    AssociatedObject.Width = minNewSizeOfParentUserControl;
    AssociatedObject.Height = minNewSizeOfParentUserControl;
  }
}
对于行为,您的xaml将如下所示:

  <Grid>
    <i:Interaction.Behaviors>
      <local:GridSquareSizeBehavior />
    </i:Interaction.Behaviors>
  </Grid>

使用Snoop测试这两种方法,并在膨胀/收缩时保持正方形大小。请注意,crux中的两种方法使用相同的逻辑只是一个快速的模型,如果在宽度改变时更新逻辑以仅更新高度,反之亦然,而不是同时更新高度和取消调整大小(如果不需要),那么您可能能够压缩一些更好的性能当网格是顶级元素时仍然不起作用,在某些情况下,它最终将控件扩展到超出可用大小的范围。因此,尝试了其他一些获得所需输出的方法

有两种方法可以解决这个问题:

由于这是一个与视图相关的问题,不破坏MVVM,保持正方形,如果您同意保留一点代码,您可以执行以下操作

private void OnSizeChanged(object sender, SizeChangedEventArgs e) {
  double minNewSizeOfParentUserControl = Math.Min(e.NewSize.Height, e.NewSize.Width);
  mainGrid.Width = minNewSizeOfParentUserControl;
  mainGrid.Height = minNewSizeOfParentUserControl;
}
在xaml中,您可以将主顶级网格命名为mainGrid,并将UserControl size changed事件处理程序附加到上述函数,而不是网格本身

然而,无论出于何种原因,如果您完全讨厌代码隐藏,那么您可以稍微花哨一点,创建一种行为,例如

public class GridSquareSizeBehavior : Behavior<Grid> {
  private UserControl _parent;

  protected override void OnAttached() {
    DependencyObject ucParent = AssociatedObject.Parent;
    while (!(ucParent is UserControl)) {
      ucParent = LogicalTreeHelper.GetParent(ucParent);
    }
    _parent = ucParent as UserControl;
    _parent.SizeChanged += SizeChangedHandler;
    base.OnAttached();
  }

  protected override void OnDetaching() {
    _parent.SizeChanged -= SizeChangedHandler;
    base.OnDetaching();
  }

  private void SizeChangedHandler(object sender, SizeChangedEventArgs e) {
    double minNewSizeOfParentUserControl = Math.Min(e.NewSize.Height, e.NewSize.Width);
    AssociatedObject.Width = minNewSizeOfParentUserControl;
    AssociatedObject.Height = minNewSizeOfParentUserControl;
  }
}
对于行为,您的xaml将如下所示:

  <Grid>
    <i:Interaction.Behaviors>
      <local:GridSquareSizeBehavior />
    </i:Interaction.Behaviors>
  </Grid>

使用Snoop测试这两种方法,并在膨胀/收缩时保持正方形大小。请注意,crux中的两种方法使用相同的逻辑只是一个快速的模型,如果将逻辑更新为仅在宽度更改时更新高度,反之亦然,而不是同时使用这两种方法并取消调整大小,则可能会获得更好的性能。如果不需要,请尝试将网格放在视口中:

下面是我想到的一个代码示例:

用户控件:

<UserControl x:Class="StackOverflow.CheckBoard"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Viewbox>
        <Grid Background="Red" Height="200" Width="200">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Button Content="testing" Grid.Row="0"/>
            <Button Content="testing" Grid.Row="1"/>
            <Button Content="testing" Grid.Row="2"/>
        </Grid>
    </Viewbox>
</UserControl>
和主窗口:

<Window x:Class="StackOverflow.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:StackOverflow"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:AllNoneCheckboxConverter x:Key="converter"/>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <local:CheckBoard MaxWidth="80"/>
        </StackPanel>
    </Grid>
</Window>

此Viewbox将根据给定的空间缩放控件。由于viewbox内的栅格是方形的,因此栅格将始终保持方形。尝试使用我在主窗口中使用的MaxWidth属性。

尝试将网格放在视口中:

下面是我想到的一个代码示例:

用户控件:

<UserControl x:Class="StackOverflow.CheckBoard"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <Viewbox>
        <Grid Background="Red" Height="200" Width="200">
            <Grid.RowDefinitions>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Button Content="testing" Grid.Row="0"/>
            <Button Content="testing" Grid.Row="1"/>
            <Button Content="testing" Grid.Row="2"/>
        </Grid>
    </Viewbox>
</UserControl>
和主窗口:

<Window x:Class="StackOverflow.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:StackOverflow"
        Title="MainWindow" Height="350" Width="525">
    <Window.Resources>
        <local:AllNoneCheckboxConverter x:Key="converter"/>
    </Window.Resources>
    <Grid>
        <StackPanel>
            <local:CheckBoard MaxWidth="80"/>
        </StackPanel>
    </Grid>
</Window>
此Viewbox将根据给定的空间缩放控件。由于viewbox内的栅格是方形的,因此栅格将始终保持方形。尝试使用我在主窗口中使用的MaxWidth属性。

Re 在任何适合您的地方注册,通常是在构造函数中或附加:

SizeChanged+=正方形; 并使用以下方法处理大小:

私有void Squareobject发送器,SizeChangedEventArgs e { 如果e.HeightChanged Width=e.NewSize.Height; 否则,如果e.WidthChanged Height=e.NewSize.Width; }
在适合您的地方注册,通常在constructor或OnAttached中注册:

SizeChanged+=正方形; 并使用以下方法处理大小:

私有void Squareobject发送器,SizeChangedEventArgs e { 如果e.HeightChanged Width=e.NewSize.Height; 否则,如果e.WidthChanged Height=e.NewSize.Width; }
使用snoop验证维度尝试了这两种方法。第一个为您提供了尺寸要求,但是如果需要,它会将一个维度拉伸到视口边界之外以保持正方形,而不是限制其他维度不展开。第二种方法看起来可以在视觉上工作,但正如所述,ViewBox会缩放子对象。然后调整大小不会更改网格的大小,而只是将子控件缩放到新的大小。第一种解决方案显然会在可用区域的宽度大于其高度时使控件的一部分不可见。我只是演示如何正确地进行绑定实际宽度而不是宽度。无论如何,Viewbox解决方案是获得方形控件的最简单方法。当然,网格永远不会调整大小,因为宽度和高度设置为固定值。我想这里根本不需要调整大小。是的,我也试过实际宽度的,在我原来的帖子中没有提到。也不管用。我想避免使用Viewbox,因为它可以缩放内部的所有内容,这并不好。例如,如果我希望网格中的一列无论如何都精确为20宽,我不能这样做,因为Viewbox将重新缩放所有内容。使用snoop验证维度尝试了这两种方法。第一个为您提供了尺寸要求,但是如果需要,它会将一个维度拉伸到视口边界之外以保持正方形,而不是限制其他维度不展开。第二种方法看起来可以在视觉上工作,但正如所述,ViewBox会缩放子对象。然后调整大小不会更改网格的大小,而只是将子控件缩放到新的大小。第一种解决方案显然会在可用区域的宽度大于其高度时使控件的一部分不可见。我只是演示如何正确地进行绑定实际宽度而不是宽度。无论如何,Viewbox解决方案是获得方形控件的最简单方法。当然,网格永远不会调整大小,因为宽度和高度设置为固定值。我想这里根本不需要调整大小。是的,我也试过实际宽度的,在我原来的帖子中没有提到。也不管用。我想避免使用Viewbox,因为它可以缩放内部的所有内容,这并不好。例如,如果我希望网格中的一列无论如何都精确为20宽,我不能这样做,因为Viewbox将重新缩放所有内容。谢谢,我将尝试一下。我从来没有使用过行为,所以我会坚持你提出的代码隐藏解决方案,它看起来很简单,实际上,在我看来,代码隐藏完全没问题。欢迎你:yeh Behaviors在某些地方有它的优势,例如,你可能想说在其他用户控件中重用它。因此,您的代码重复更少,并且可以不在xaml中命名网格。归结到具体的用例。祝你好运,我来试试。我从来没有使用过行为,所以我会坚持你提出的代码隐藏解决方案,它看起来很简单,实际上,在我看来,代码隐藏完全没问题。欢迎你:yeh Behaviors在某些地方有它的优势,例如,你可能想说在其他用户控件中重用它。因此,您的代码重复更少,并且可以不在xaml中命名网格。归结到具体的用例。很好luckAre您确定当您将SizeChanged处理程序直接应用到网格时,您的“编辑为建议”解决方案也可以工作吗?当我对此进行测试时,网格在初始调用之后停止接收任何大小更改的调用,您可以通过代码在其上设置宽度和高度。因此,从现在起,任何重新调整大小都只是定位网格中心,不再有实际大小更改。使用Math.Min时,逻辑更加清晰,但是我必须将SizeChanged处理程序应用于父级“UserControl”,而不是应用于网格本身。我也用新的逻辑更新了我的帖子。啊,很好的一点,我没有注意到这个副作用,因为我的网格在我的应用程序中没有再次调整大小。你知道这种行为是从哪里来的吗?今晚我将在我的代码中尝试,并将相应地更新我的帖子。谢谢你的跟进,没问题。不太确定它来自何处,野生猜测可能是SizeChanged处理程序在调用之前在事件链中被忽略,当相关元素具有固定维度时,因此我们只看到初始调用,从而禁用未来调用。正确。当您设置固定大小时,它将不再自动更改大小
y、 因此SizeChanged将不再被调用。您需要做的是将网格命名为Board,然后将SizeChanged事件连接到控件本身。当控件更改大小时,您可以在上面的代码中使用Board.Width=size。当您将SizeChanged处理程序直接应用于网格时,您是否确定建议中的“编辑”解决方案仍然有效?当我对此进行测试时,网格在初始调用之后停止接收任何大小更改的调用,您可以通过代码在其上设置宽度和高度。因此,从现在起,任何重新调整大小都只是定位网格中心,不再有实际大小更改。使用Math.Min时,逻辑更加清晰,但是我必须将SizeChanged处理程序应用于父级“UserControl”,而不是应用于网格本身。我也用新的逻辑更新了我的帖子。啊,很好的一点,我没有注意到这个副作用,因为我的网格在我的应用程序中没有再次调整大小。你知道这种行为是从哪里来的吗?今晚我将在我的代码中尝试,并将相应地更新我的帖子。谢谢你的跟进,没问题。不太确定它来自何处,野生猜测可能是SizeChanged处理程序在调用之前在事件链中被忽略,当相关元素具有固定维度时,因此我们只看到初始调用,从而禁用未来调用。正确。当您设置固定大小时,它将不再自动更改大小,因此SizeChanged将不再被调用。您需要做的是将网格命名为Board,然后将SizeChanged事件连接到控件本身。当控件更改大小时,您可以在上面的代码中使用Board.Width=size。您好,谢谢您的回答。但正如我在最初的帖子中提到的,我不想使用ViewBox,因为它会把事情搞砸。我在网格内有很多控件,如果我使用Viewbox,网格肯定是方形的,但它内的控件也会缩放。另外,在使用Viewbox时,行和内容的行为会以某种方式变得一团糟。我没有做更多的调查,这是如此混乱,我想要一个完全不同的解决方案。谢谢你,真是抱歉。我一定错过了:嗨,谢谢你的回答。但正如我在最初的帖子中提到的,我不想使用ViewBox,因为它会把事情搞砸。我在网格内有很多控件,如果我使用Viewbox,网格肯定是方形的,但它内的控件也会缩放。另外,在使用Viewbox时,行和内容的行为会以某种方式变得一团糟。我没有做更多的调查,这是如此混乱,我想要一个完全不同的解决方案。谢谢你,真是抱歉。我一定错过了: