创建一个方形UserControl

时间:2013-03-24 19:44:00

标签: wpf user-controls grid microsoft-metro winrt-xaml

我正在设计一个需要为方形的用户控件,并填充给出的空间(给出一些上下文,它是一个检查板)。

我的用户控件如下:

<Grid>
    <!-- My 8 lines / colums, etc. , sized with "1*" to have equal lines -->
</Grid>

现在我只想说“无论它有什么扩展空间,这个网格都必须是正方形”。

徒劳地尝试解决方案:

  1. 我不能使用UniformGrid,因为我实际上有这些行的名称。另外,我有一个不同大小的前导标题行和列。

  2. 如果我将ViewboxUniform一起使用,那么一切都会混乱。

  3. 我尝试使用经典

    <Grid Height="{Binding RelativeSource={RelativeSource Self}, Path=Width}"> ... </Grid>
    
  4. 但只有在我手动设置Width属性时才有效。否则,将忽略此约​​束。

    结论

    我不在乎,我真的想避免手动设置Width / Height,因为此控件可用于多个不同的地方(ListItem模板,游戏,等...)。


    来自建议的解决方案:

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

    网格现在是:

    <Grid SizeChanged="Board_FullControlSizeChanged">...</Grid>
    

    事件处理程序是:

    private void Board_FullControlSizeChanged(object sender, SizeChangedEventArgs args)
    {
        double size = Math.min (args.NewSize.Height, args.NewSize.Width);
        ((Grid)sender).Width = size;
        ((Grid)sender).Height = size;
    }
    

4 个答案:

答案 0 :(得分:3)

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

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

因为这是一个视图相关的问题(不破坏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尺寸更改事件处理程序附加到上述函数而不是Grid本身。

但是,如果你因任何原因完全讨厌代码隐藏,你可能会更加花哨并创建一种行为,例如

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测试这两种方法,并在扩展/收缩时保持方形尺寸。请注意,关键字中的两种方法都使用相同的逻辑(只是一个快速的模型),如果更新逻辑只更新宽度更改宽度,反之亦然,并且取消两者,您可能会挤出一些更好的性能如果不需要,一起调整大小

答案 1 :(得分:1)

尝试将网格放入ViewBox:http://msdn.microsoft.com/en-us/library/system.windows.controls.viewbox.aspx

这是我提出的代码示例:

usercontrol:

<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将执行的操作是将控件缩放到给定的空间。由于视图框内的网格是方形的,因此网格始终保持正方形。尝试使用我在MainWindow中使用的MaxWidth属性。

答案 2 :(得分:0)

您可以将Height属性绑定到ActualWidth而不是Width

<Grid Height="{Binding ActualWidth, RelativeSource={RelativeSource Self}}">
    ...
</Grid>

但是,更好的解决方案是使用Viewbox。避免它“混乱”的诀窍是通过为宽度和高度定义(合理的)相等值来使其子方格:

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

答案 3 :(得分:0)

在适合您的位置注册(通常在构造函数或OnAttached()中):

SizeChanged += Square;

并使用此值处理大小:

private void Square(object sender, SizeChangedEventArgs e)
{
    if (e.HeightChanged) Width = e.NewSize.Height;
    else if (e.WidthChanged) Height = e.NewSize.Width;
}