在 MVVM 中动态填充 Wrappannel

发布于 2024-10-25 19:00:34 字数 3638 浏览 1 评论 0原文

这是关于使用 MVVM 的最佳实践建议。

当元素放入包装面板时,我需要填充包装面板。元素并不统一,可以是标签,也可以是文本框。根据参数的值,添加的元素会有所不同。

我在后面的代码中做到了这一点。现在我正在将整个事情转移到 MVVM 模型,但我坚持在不影响 MVVM 核心原理的情况下执行此操作的方法。在这段代码中,我将 UI 元素和逻辑内容放在一起,它们紧密相关;我已经无法将两者分开来适应 MVVM。

我尝试在VM中创建UI元素,填充UIElement类型的ObservableCollection并将其绑定到itemssource属性(随后我将wrappanel更改为listview以在整个过程中有效)。但这并没有成功,因为当我绑定元素时,代码无法理解哪个 UIelement。

下面发布的是我需要分开的代码部分:

private void CreateVisulaQueryContent() {

            VisualQueryObject visualQueryData = new VisualQueryObject();

            VisualQueryObject helperVisualQueryObject = DraggedData as    VisualQueryObject;


            //***Taking a copy of the static DraggedData object to be bound

                visualQueryData.ColumnDiscriptor = helperVisualQueryObject.ColumnDiscriptor;

            visualQueryData.ComparedValue = helperVisualQueryObject.ComparedValue;

            visualQueryData.JoinWithColumnDescriptor = helperVisualQueryObject.JoinWithColumnDescriptor;

            visualQueryData.LabelType = helperVisualQueryObject.LabelType;
            visualQueryData.OperatorValue = helperVisualQueryObject.OperatorValue;


            if (visualQueryData.LabelType == "column")
            {

                ColumnDescriptionObject descriptionValue = visualQueryData.ColumnDiscriptor;
                Label droppedElement = new Label();

                Binding binding = new Binding();
                binding.Source = visualQueryData;
                binding.Path = new PropertyPath("ColumnDiscriptor");
                binding.Mode = BindingMode.TwoWay;
                droppedElement.SetBinding(Label.DataContextProperty, binding);

                droppedElement.Content = visualQueryData.ColumnDiscriptor.TableName + "." + visualQueryData.ColumnDiscriptor.ColumnName;


                droppedElement.Foreground = Brushes.White;
                droppedElement.Background = Brushes.DarkOrange;

                droppedElement.BorderThickness = new Thickness(5);

                droppedLabel.MouseDoubleClick += columnLabel_MouseDown;
                ViewUIElements.Add(droppedElement);

            }
            else if (visualQueryData.LabelType == "controller")
            {

                Label droppedElement = new Label();

                Binding binding = new Binding();
                binding.Source = visualQueryData;
                binding.Path = new PropertyPath("OperatorValue");
                binding.Mode = BindingMode.TwoWay;
                droppedElement.SetBinding(Label.DataContextProperty, binding);


                droppedElement.Content = draggedContent.OperatorValue;
                droppedElement.Foreground = Brushes.White;
                droppedElement.Background = Brushes.Crimson;
                droppedElement.BorderThickness = new Thickness(5);

                droppedElement.MouseDoubleClick += columnLabel_MouseDown;

                ViewUIElements.Add(new Label());

            }
            else if (visualQueryData.LabelType == "value")
            {
                TextBox droppedElement = new TextBox();

                Binding binding = new Binding();
                binding.Source = visualQueryData;
                binding.Path = new PropertyPath("ComparedValue");
                binding.Mode = BindingMode.TwoWay;
                droppedElement.SetBinding(TextBox.TextProperty, binding);

               droppedElement.MouseDoubleClick += columnLabel_MouseDown;

                ViewUIElements.Add(droppedElement);
            }

            QueryDesignerModel.QueryDesignHelperCollection.Add(visualQueryData);

    }

非常感谢任何帮助!

this is regarding a best practice advice regarding using MVVM.

I am in need to populate a wrappanel as and when elements are dropped in to it. The elements are not uniform, they can be either labels or text boxes. Depending on a value of a parameter the element added varies.

I did this in code behind. Now I am in the process of moving the entire thing to an MVVM model am stuck with the way to do this without affecting the MVVM core principals. In this code I have both UI elements and logic content to gether which are tightly related; and I have become incapable to seperate the two to suite MVVM.

I tried creating the UI elements in the VM, populating a ObservableCollection of type UIElement and binding it to the itemssource property (Subsequently I changed wrappanel to be a listview to be effective in the whole thing). But this did not work out since when I bind the elements there is no way the code can understand which UIelement.

Posted below is the section of the code which I need to seperate:

private void CreateVisulaQueryContent() {

            VisualQueryObject visualQueryData = new VisualQueryObject();

            VisualQueryObject helperVisualQueryObject = DraggedData as    VisualQueryObject;


            //***Taking a copy of the static DraggedData object to be bound

                visualQueryData.ColumnDiscriptor = helperVisualQueryObject.ColumnDiscriptor;

            visualQueryData.ComparedValue = helperVisualQueryObject.ComparedValue;

            visualQueryData.JoinWithColumnDescriptor = helperVisualQueryObject.JoinWithColumnDescriptor;

            visualQueryData.LabelType = helperVisualQueryObject.LabelType;
            visualQueryData.OperatorValue = helperVisualQueryObject.OperatorValue;


            if (visualQueryData.LabelType == "column")
            {

                ColumnDescriptionObject descriptionValue = visualQueryData.ColumnDiscriptor;
                Label droppedElement = new Label();

                Binding binding = new Binding();
                binding.Source = visualQueryData;
                binding.Path = new PropertyPath("ColumnDiscriptor");
                binding.Mode = BindingMode.TwoWay;
                droppedElement.SetBinding(Label.DataContextProperty, binding);

                droppedElement.Content = visualQueryData.ColumnDiscriptor.TableName + "." + visualQueryData.ColumnDiscriptor.ColumnName;


                droppedElement.Foreground = Brushes.White;
                droppedElement.Background = Brushes.DarkOrange;

                droppedElement.BorderThickness = new Thickness(5);

                droppedLabel.MouseDoubleClick += columnLabel_MouseDown;
                ViewUIElements.Add(droppedElement);

            }
            else if (visualQueryData.LabelType == "controller")
            {

                Label droppedElement = new Label();

                Binding binding = new Binding();
                binding.Source = visualQueryData;
                binding.Path = new PropertyPath("OperatorValue");
                binding.Mode = BindingMode.TwoWay;
                droppedElement.SetBinding(Label.DataContextProperty, binding);


                droppedElement.Content = draggedContent.OperatorValue;
                droppedElement.Foreground = Brushes.White;
                droppedElement.Background = Brushes.Crimson;
                droppedElement.BorderThickness = new Thickness(5);

                droppedElement.MouseDoubleClick += columnLabel_MouseDown;

                ViewUIElements.Add(new Label());

            }
            else if (visualQueryData.LabelType == "value")
            {
                TextBox droppedElement = new TextBox();

                Binding binding = new Binding();
                binding.Source = visualQueryData;
                binding.Path = new PropertyPath("ComparedValue");
                binding.Mode = BindingMode.TwoWay;
                droppedElement.SetBinding(TextBox.TextProperty, binding);

               droppedElement.MouseDoubleClick += columnLabel_MouseDown;

                ViewUIElements.Add(droppedElement);
            }

            QueryDesignerModel.QueryDesignHelperCollection.Add(visualQueryData);

    }

Any help is deeply appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

守护在此方 2024-11-01 19:00:34

正如我所承诺的,我创建了一个示例,其中 ViewModels 内没有 UIElements

首先,我从你的方法中删除了很多代码:

public class MainViewModel
{
    public MainViewModel()
    {
        //For demonstration
        this.ViewUIElements = new ObservableCollection<VisualQueryObject>
        {
            new VisualQueryObject{LabelType = "column", ColumnDiscriptor = new DescriptionModel("Table1", "Column2") },
            new VisualQueryObject{LabelType = "controller"},
            new VisualQueryObject{LabelType = "value"},
        };
    }

    public void UpdateCollection(VisualQueryObject helperVisualQueryObject)
    {
        VisualQueryObject visualQueryData = new VisualQueryObject();
        //I would remove copying, but maybe it is intended behavior
        //***Taking a copy of the static DraggedData object to be bound           
        visualQueryData.ColumnDiscriptor = helperVisualQueryObject.ColumnDiscriptor;
        visualQueryData.ComparedValue = helperVisualQueryObject.ComparedValue;
        visualQueryData.JoinWithColumnDescriptor = helperVisualQueryObject.JoinWithColumnDescriptor;
        visualQueryData.LabelType = helperVisualQueryObject.LabelType;
        visualQueryData.OperatorValue = helperVisualQueryObject.OperatorValue;

        this.ViewUIElements.Add(visualQueryData);

        //QueryDesignerModel.QueryDesignHelperCollection.Add(visualQueryData);   //I don't know what this method does
    }

    public ObservableCollection<VisualQueryObject> ViewUIElements { get; private set; }
}

然后我创建了 DataTemplateSelector 类,我在你的问题中的函数中放置了 if 子句:

public class QueryObjectDateTemplateSelector : DataTemplateSelector
{
    public DataTemplate ColumnTemplate { get; set; }

    public DataTemplate ControllerTemplate { get; set; }

    public DataTemplate ValueTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
    {
        var visualQueryData = item as VisualQueryObject;
        if (visualQueryData == null)
            return null;

        if (visualQueryData.LabelType == "column")
            return ColumnTemplate;
        else if (visualQueryData.LabelType == "controller")
            return ControllerTemplate;
        else if (visualQueryData.LabelType == "value")
            return ValueTemplate;
        else return null;

    }
}

这几乎就是全部。其他所有内容都在 xaml 中:

<Window.Resources>
    <ItemsPanelTemplate x:Key="WrapPanelTemplate">
        <WrapPanel Orientation="Horizontal" />
    </ItemsPanelTemplate>

    <DataTemplate x:Key="ColumnDataTemplate">
        <Label DataContext="{Binding ColumnDiscriptor}" Foreground="White" Background="DarkOrange" BorderThickness="5">
            <TextBlock>
                <Run Text="{Binding TableName}"/><Run Text="."/><Run Text="{Binding ColumnName}"/> 
            </TextBlock>
        </Label>
    </DataTemplate>
    <DataTemplate x:Key="ControllerDataTemplate">
        <Label Content="Controller"/>
    </DataTemplate>
    <DataTemplate x:Key="ValueDataTemplate">
        <TextBox Text="Value"/>
    </DataTemplate>
    <local:QueryObjectDateTemplateSelector x:Key="ModelSelector"
                                           ColumnTemplate="{StaticResource ColumnDataTemplate}"
                                           ControllerTemplate="{StaticResource ControllerDataTemplate}"
                                           ValueTemplate="{StaticResource ValueDataTemplate}"/>
</Window.Resources>
<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>

<Grid>
    <ItemsControl ItemsSource="{Binding ViewUIElements}" ItemsPanel="{StaticResource WrapPanelTemplate}" 
                  ItemTemplateSelector="{StaticResource ModelSelector}"/>
</Grid>

我只编码了列模板,其他模板也应该在 xaml 中重写。
所以现在你可以调用这个方法:

((MainViewModel)this.DataContext).UpdateCollection(DraggedData as VisualQueryObject);

As I promised, I have created an example in which there is no UIElements inside ViewModels.

First of all, I removed lot of code from your method:

public class MainViewModel
{
    public MainViewModel()
    {
        //For demonstration
        this.ViewUIElements = new ObservableCollection<VisualQueryObject>
        {
            new VisualQueryObject{LabelType = "column", ColumnDiscriptor = new DescriptionModel("Table1", "Column2") },
            new VisualQueryObject{LabelType = "controller"},
            new VisualQueryObject{LabelType = "value"},
        };
    }

    public void UpdateCollection(VisualQueryObject helperVisualQueryObject)
    {
        VisualQueryObject visualQueryData = new VisualQueryObject();
        //I would remove copying, but maybe it is intended behavior
        //***Taking a copy of the static DraggedData object to be bound           
        visualQueryData.ColumnDiscriptor = helperVisualQueryObject.ColumnDiscriptor;
        visualQueryData.ComparedValue = helperVisualQueryObject.ComparedValue;
        visualQueryData.JoinWithColumnDescriptor = helperVisualQueryObject.JoinWithColumnDescriptor;
        visualQueryData.LabelType = helperVisualQueryObject.LabelType;
        visualQueryData.OperatorValue = helperVisualQueryObject.OperatorValue;

        this.ViewUIElements.Add(visualQueryData);

        //QueryDesignerModel.QueryDesignHelperCollection.Add(visualQueryData);   //I don't know what this method does
    }

    public ObservableCollection<VisualQueryObject> ViewUIElements { get; private set; }
}

Then I created the DataTemplateSelector class where I've put if-clauses from the function in your question:

public class QueryObjectDateTemplateSelector : DataTemplateSelector
{
    public DataTemplate ColumnTemplate { get; set; }

    public DataTemplate ControllerTemplate { get; set; }

    public DataTemplate ValueTemplate { get; set; }

    public override DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
    {
        var visualQueryData = item as VisualQueryObject;
        if (visualQueryData == null)
            return null;

        if (visualQueryData.LabelType == "column")
            return ColumnTemplate;
        else if (visualQueryData.LabelType == "controller")
            return ControllerTemplate;
        else if (visualQueryData.LabelType == "value")
            return ValueTemplate;
        else return null;

    }
}

And that's almost all. Everything else is in xaml:

<Window.Resources>
    <ItemsPanelTemplate x:Key="WrapPanelTemplate">
        <WrapPanel Orientation="Horizontal" />
    </ItemsPanelTemplate>

    <DataTemplate x:Key="ColumnDataTemplate">
        <Label DataContext="{Binding ColumnDiscriptor}" Foreground="White" Background="DarkOrange" BorderThickness="5">
            <TextBlock>
                <Run Text="{Binding TableName}"/><Run Text="."/><Run Text="{Binding ColumnName}"/> 
            </TextBlock>
        </Label>
    </DataTemplate>
    <DataTemplate x:Key="ControllerDataTemplate">
        <Label Content="Controller"/>
    </DataTemplate>
    <DataTemplate x:Key="ValueDataTemplate">
        <TextBox Text="Value"/>
    </DataTemplate>
    <local:QueryObjectDateTemplateSelector x:Key="ModelSelector"
                                           ColumnTemplate="{StaticResource ColumnDataTemplate}"
                                           ControllerTemplate="{StaticResource ControllerDataTemplate}"
                                           ValueTemplate="{StaticResource ValueDataTemplate}"/>
</Window.Resources>
<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>

<Grid>
    <ItemsControl ItemsSource="{Binding ViewUIElements}" ItemsPanel="{StaticResource WrapPanelTemplate}" 
                  ItemTemplateSelector="{StaticResource ModelSelector}"/>
</Grid>

I have coded only the Column Template, other templates should be rewritten in xaml too.
So now you can call this method:

((MainViewModel)this.DataContext).UpdateCollection(DraggedData as VisualQueryObject);
只是一片海 2024-11-01 19:00:34

我不太确定您想要什么,但听起来您想使用数据模板根据底层绑定数据对象类型更改 ListBoxItemTemplate

因此,在您的情况下,您可以拥有一个 ObservableCollection,其中标签类型具有每个标签类型(列、控制器、值)的派生类型,然后使用隐式数据模板(使用 DataType 属性)为每个子类型选择合适的模板。

或者,您可以使用一个带有对象类型枚举的 LabelType

ObservableCollection<LabelType> Labels ...

Labels.Add(new ControllerLabelType());

...

<ListBox ItemsSource="{Binding Labels}">
  <ListBox.Resources>
    <DataTemplate DataType="{x:Type local:ControllerLabelType}">
      <TextBlock Text="{Binding OperatorValue}" />
    </DataTemplate>
    <DataTemplate DataType="{x:Type local:ValueLabelType}">
      <TextBox Text="{Binding OperatorValue}" />
    </DataTemplate>        
  </ListBox.Resources>
  <ListBox.ItemsPanel>
    <ItemsPanelTemplate>
      <WrapPanel IsItemsHost="True" />
    </ItemsPanelTemplate>
  </ListBox.ItemsPanel>
</ListBox>    

请参阅 http://msdn.microsoft.com/en-us/library/ms742521。有关数据模板的更多信息,请访问 http://www.aspx

I'm not quite sure what you are after, but it sounds like you want to use data templating to change the ItemTemplate for a ListBox based on the underlying bound data object type.

So, in your case you can have an ObservableCollection<LabelType> where label type has derived types for each of your label types (column, controller, value), then use implicit data templates (using the DataType property) to select an appropriate template for each sub type.

Alternatively, you could have one LabelType with an enumeration for the type of object.

ObservableCollection<LabelType> Labels ...

Labels.Add(new ControllerLabelType());

...

<ListBox ItemsSource="{Binding Labels}">
  <ListBox.Resources>
    <DataTemplate DataType="{x:Type local:ControllerLabelType}">
      <TextBlock Text="{Binding OperatorValue}" />
    </DataTemplate>
    <DataTemplate DataType="{x:Type local:ValueLabelType}">
      <TextBox Text="{Binding OperatorValue}" />
    </DataTemplate>        
  </ListBox.Resources>
  <ListBox.ItemsPanel>
    <ItemsPanelTemplate>
      <WrapPanel IsItemsHost="True" />
    </ItemsPanelTemplate>
  </ListBox.ItemsPanel>
</ListBox>    

See http://msdn.microsoft.com/en-us/library/ms742521.aspx for more on data templating.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文