Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 具有不同数量对象的WPF MVVM视图。如何?_C#_Wpf_Mvvm - Fatal编程技术网

C# 具有不同数量对象的WPF MVVM视图。如何?

C# 具有不同数量对象的WPF MVVM视图。如何?,c#,wpf,mvvm,C#,Wpf,Mvvm,嗨! 我想设计在不同位置包含多个对象的视图。例如,如果viewmodel可以有类似字段的对象列表(矩形),并且当我更改/添加列表中的成员时,新的矩形将在视图中的指定位置出现,这将非常好。 如何创建这样的视图/视图模型?您可以在视图模型中拥有ICollectionView或ObservableCollection属性,并将ItemsControl的ItemsSource属性绑定到此属性。然后将显示集合(视图)中的所有项目。但是,它通常会在StackPanel中显示它们,因为这是ItemsContr

嗨! 我想设计在不同位置包含多个对象的视图。例如,如果viewmodel可以有类似字段的对象列表(矩形),并且当我更改/添加列表中的成员时,新的矩形将在视图中的指定位置出现,这将非常好。
如何创建这样的视图/视图模型?

您可以在视图模型中拥有
ICollectionView
ObservableCollection
属性,并将
ItemsControl
ItemsSource
属性绑定到此属性。然后将显示集合(视图)中的所有项目。但是,它通常会在
StackPanel
中显示它们,因为这是
ItemsControl
的默认项目容器。就我理解你的问题而言,你想把项目放在屏幕上的任何地方。这可以通过使用
Canvas
作为
ItemsControl
ItemsPanel
来实现,然后将
Canvas.Left
Canvas.Top
属性绑定到ViewModels中的属性。当然,每个项目都需要
Left
Top
属性(也可能需要
Width
Height
属性)


代码的描述已经在XAML注释中。但是,我应该注意,我使用了来自的
ExpressionConverter
。我从我的一个应用程序中复制并粘贴了上面的代码,因此其中可能存在一些不一致之处,因为需要根据您的场景快速调整属性。但是,我认为原则应该是明确的。祝你好运

接受答案的一个问题是Canvas.Left/Top绑定不起作用,因为矩形由ItemsControl包装在容器控件中。另一个问题涉及这一问题:


希望这能对其他人有所帮助,因为我在屏幕上使劲敲着脑袋想知道为什么这不起作用。

谢谢gehho,你也在这里回答了我的问题:如果你想复制/粘贴你的答案,我会接受。这是一个很棒的代码!相关坐标将是最好的选择!您是否计划使用valueconverter,或者是否还有其他方法?ItemTemplate是如何工作的?为什么要将ItemsControl与canvas一起使用,而不是将内容设置为ItemsControl的canvas。这个设计对我来说不清楚。。O_oI将很快添加一个带有相对坐标的示例。然而,这需要更多的工作。ItemsControl用于动态显示和删除多个项目的所有场景(顺便说一句:ListBox和许多其他控件派生自ItemsControl)。ItemsPanel属性定义ItemsControl中用于排列项目的面板。默认设置为StackPanel,因此项目会一个接一个地排列。我们将其更改为画布,这样就可以通过指定Canvas.Left和Canvas.Top来任意排列项目。顺便说一句:您可以在以下位置找到关于ItemsControl的许多有用信息!我用一个使用相对坐标的例子更新了答案。我希望这有帮助。
public class ItemViewModel
{
    public double Left { get; set; }
    public double Top { get; set; }
    public double Width { get; set; }
    public double Height { get; set; }

    // whatever you need...
}

public class CollectionViewModel
{
    public ObservableCollection<ItemViewModel> Collection { get; }

    // some code which fills the Collection with items
}
<ItemsControl ItemsSource="{Binding Collection}">

    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <ItemsControl.ItemTemplate>
        <DataTemplate DataType="{x:Type local:ItemViewModel}">
            <Rectangle Width="{Binding Width}" Height="{Binding Height}"
                       Canvas.Left="{Binding Left}" Canvas.Top="{Binding Top}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>

</ItemsControl>
<DataTemplate DataType="{x:Type local:ItemViewModel}">
    <Rectangle Width="{Binding Width}" Height="{Binding Height}">

        <!-- Make the left position of the item depend on the ActualWidth of the Canvas,
             the relative Left position (between 0 and 1) from the ItemViewModel, and the ActualWidth
             of the item itself. This is needed because the Canvas.Left property defines the
             position of the left side, not the center. Therefore, we calculate the position of
             the center this way:
                  (Canvas.ActualWidth * ItemViewModel.Left) - (Item.ActualWidth / 2)
        -->
        <Canvas.Left>
            <MultiBinding>
                <MultiBinding.Converter>
                    <converters:ExpressionConverter Expression="{}({0} * {1}) - ({2} / 2)"/>
                </MultiBinding.Converter>
                <Binding Path="ActualWidth" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Canvas}}"/>
                <Binding Path="Left"/>
                <Binding Path="ActualWidth" RelativeSource="{RelativeSource Self}"/>
            </MultiBinding>
        </Canvas.Left>

        <!-- the top position of the items is determined the same way as the left position
             which is described above -->
        <Canvas.Top>
            <MultiBinding>
                <MultiBinding.Converter>
                    <converters:ExpressionConverter Expression="{}({0} * {1}) - ({2} / 2)"/>
                </MultiBinding.Converter>
                <Binding Path="ActualHeight" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type Canvas}}"/>
                <Binding Path="Top"/>
                <Binding Path="ActualHeight" RelativeSource="{RelativeSource Self}"/>
            </MultiBinding>
        </Canvas.Top>

    </Rectangle>
</DataTemplate>