WPF用户控件的布局作为运行时

6
在WPF中,我想创建一个窗口,看起来像下面这样:

应用程序与用户控件 http://www.freeimagehosting.net/uploads/86209e1a87.png

屏幕上有四个用户控件,#1、2、3、4。正如您所看到的,用户控件2不应该被呈现为一个框,而是应该内嵌。

如果这是一个WPF流文档:

  • 1、3、4将成为一个段落(装箱)
  • 2个运行(内联)

原因是2可以在另一个表单中使用,而不需要拆分3。

你有任何好的方法吗?

已经想到了一些点子:

  • 2是一个普通的用户控件(装箱)。当它放置在窗口中时,2、3、4被放置在画布中,使用它们的Z-Order和边距来控制它们的呈现方式
  • 2已经格式化了一个网格,以便它可以接受3和4作为ContentControl,并通过Xaml或代码注入它们
  • 2将主网格公开为属性,并通过附加属性的好处添加3和4的数据
  • 我们创建自己的布局控件,并实现排列和测量方法来创建一个像运行一样的布局

还有一些不太干净的想法......

你有什么想法吗?

谢谢,

帕特里克

7个回答

1

我认为你的表单格式不正确,就我个人而言。

第二区域不应包括你描述的底部部分,你要让它“内联”。

在布局上它应该是分开的,并且可能与第三区和第四区一起适合一个有2列和2行的网格中。第3区域然后需要具有2个合并单元格。

如果用这种方式完成,XAML实际上会非常清洁。

它可能看起来像这样:

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition />
        <RowDefinition />
        <RowDefinition />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
    </Grid.ColumnDefinitions>

    <uC:Area1 Grid.Row="0"><!-- Area Control here --></uC:Area1>
    <uC:Area2 Grid.Row="1"><!-- Area Control here --></uC:Area2>

    <Grid Grid.Row="2">
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <uC:AreaDuree Grid.Row="0" Grid.Column="0">
            <!-- Area Control here -->
        </uC:AreaDuree>

        <uC:Area4 Grid.Row="1" Grid.Column="0">
            <!-- Area Control here -->
        </uC:Area4>

        <uC:Area3 Grid.RowSpan="2" Grid.Column="1">
            <!-- Area Control here -->
        </uC:Area3>                        

    </Grid>

</Grid>

是的,那将是很好的。我编辑了问题以注明#2可以在另一个地方使用。 - PBelanger
@PBelanger,这很好,但这意味着您的用户控件应进一步封装,以便您可以更改它们各自的布局。将Area2的底部部分作为其自己的控件允许您将其放置在任何数量的布局方案中的任何位置。 - Joseph

1

我很惊讶还没有人建议使用实际的FlowDocument来布局你的用户界面。

我以前使用过FlowDocument来实现复杂的排列。它的效果非常好,只是某些属性继承被阻止(例如TextElement.FontSize)。这并不难解决。

在你的情况下,只需将第2点拆分为每个部分一个单独的UserControl,并在FlowDocument中使用InlineUIContainer来包装每个部分。

<Window>
  <DockPanel>
    ... toolbars, etc ...

    <FlowDocumentScrollViewer ...>
      <FlowDocument>
        ...
         <InlineUIContainer>
           <my:PartialNumberTwo DataContext="{Binding ...}" />
         </InlineUIContainer>
        ...

当然,FlowDocument存在一些限制,所以它可能无法适用于您的情况。

一个非常灵活的选择是创建一个自定义面板,该面板将其子元素布局在WrapPanel样式中,但是通过拟合矩形来完成,并在开始之前标记任何被面板自身兄弟覆盖的区域为不可用。

这样的面板将被使用如下:

<Grid ...>
  ... RowDefinitions, ColumnDefinitions ...

  <panels:WrapIntoRemainingSpacePanel RowSpan="4" ColumnSpan="3"> <!-- fill whole grid -->
    <my:Control2Part1 />
    <my:Control2Part2 />
    <my:Control2Part3 />
    <my:Control2Part4 />
 </panels:WrapIntoRemainingSpacePanel>

 <my:Control1 Grid.Row="0" Grid.ColumnSpan="3" />
 <my:Control3 Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="2" />
 <my:Control4 Grid.Row="3" Grid.RowSpan="2" />

该面板将按以下方式实现:

  1. 在ArrangeOverride中,安排一个Dispatcher.BeginInvoke回调来执行实际的排列并报告使用的完整大小
  2. 在回调中,获取表示所有同级边界框的矩形在父坐标空间中的矩形。
  3. 对放置的所有矩形的Y坐标(顶部和底部)进行排序
  4. 通过查找排序列表中第一个存在内容水平空间的Y坐标,并将其尽可能向左放置,来放置每个子项
  5. 监视同级VisualTransform的更改,如果有任何更改,则调用InvalidateArrange()

1
我建议您将Control #2拆分为3或4个更具体的UserControl。然后在WrapPanel中加载#2,#3,#4。请参考link: UserControl的布局来实现智能Wrap Panel排列和测量策略。

1

为了简化设计师的工作,我建议您使用ContentControl解决方案。

这样,您的设计师可以轻松地添加他们的控件,而对于您来说,只需确保您的Usercontrol #2足够智能,以正确的方式呈现您的布局。


0

我认为这是一个简单的网格,"诀窍"在于允许一个控件覆盖另一个控件- WPF将始终以相同的顺序呈现它们:

我对所有网格行使用了Height="Auto",根据您的用户控件设置方式,您可能需要指定实际高度才能正确排列它们。

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"/>
        <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <uc:UserControl1 Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2"/>
    <uc:UserControl2 Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Grid.RowSpan="2"/>
    <uc:UserControl3 Grid.Row="2" Grid.Column="1" Grid.RowSpan="2"/>
    <uc:UserControl4 Grid.Row="3" Grid.Column="0"/>
<Grid>

0

这是一个大网格,有3行2列。该网格的每个区域都包含一个可以接收用户控件的网格。因此,网格号3只需要将其顶部边距设置为-75即可。如果在xaml中将其放置得更低,则它将重叠网格号2。然后,您只需要锁定列和行,具体取决于您希望它如何反应。

<Grid x:Name="LayoutRoot">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="0.55*"/>
        <ColumnDefinition Width="0.45*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="0.363*"/>
        <RowDefinition Height="0.369*"/>
        <RowDefinition Height="0.268*"/>
    </Grid.RowDefinitions>
    <Grid Grid.ColumnSpan="2" Background="#FF48C5D0"/>
    <Grid Margin="1,0,0,0" Grid.ColumnSpan="2" Grid.Row="1" Background="#FFD2A268"/>
    <Grid Margin="1,0,0,0" Grid.Row="2" Background="#FFB7F075"/>
    <Grid Grid.Column="1" Grid.Row="2" Background="#FFB129EC" Margin="0,-75,0,0"/>
</Grid>

马丁·拉蒙塔涅


-1

如果他提出这个问题,很可能是因为他需要单独使用UserControl。问题是:“如何根据上下文在运行时自动移动/调整UserControl/Panel的大小?”,而不是如何用另一种方式实现它。 - Cédric Boivin
我不知道为什么你要给这个投票反对?我的解决方案与@Joseph添加XAML的解决方案非常相似,只是在同一视觉表示上。 - Jobi Joy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接