绑定动态添加的元素

时间:2013-02-15 17:37:43

标签: wpf xaml

TPTB决定我们的应用必须在一个窗口中运行,不允许在模式模式下弹出新窗口。

当然,我们有一个UI设计,涉及在整个地方弹出模态对话框。

所以我在窗口添加了顶级网格。在该网格中,我没有定义任何行或列,因此所有内容都在第0行/第0列中绘制。

Grid中的第一个元素是另一个Grid,其中包含通常在Window中显示的所有内容。第二个是全尺寸边框,背景为灰色,半透明。其余的是具有宽边距和白色背景的边框,包含需要显示为弹出窗口的各种UserControl。除第一个之外的所有人都有Visibility =“Collapsed”。

然后,当我需要显示一个弹出窗口时,我会在灰色背景和相应的UserControl上设置Visibility =“Visible”。结果是一个很好的影子盒效果,工作正常。

直到有人决定弹出窗口需要能够显示弹出窗口。以不可预测的顺序。

我在网格中使用Visibility =“Collapsed”元素实现的方法的局限性在于它们的顺序是固定的。 UserControlB将始终显示在UserControlA之上,即使它是要求显示UserControlA的UserControlB也是如此。这是不可接受的。

所以我的下一次尝试是在Window.Resources中定义各种UserControl,并在代码中将它们添加到Grid中:

this.masterGrid.Children.Add(this.Resources["userControlA"] as UserControlA);

这几乎可行。但绑定都搞砸了。

作为示例,其中一个控件应该将Property绑定到Window的viewmodel的成员对象中的集合的CurrentItem。当我将控件定义为Grid中的隐形项时,它工作正常。但是当我将它定义为资源时,属性为空 - 它从未被绑定。

所以我在将它添加到网格后尝试将其绑定在代码中:

userControlA.SetBinding(UserControlA.myProperty, new Binding()
    { Source = this.viewModel.myCollection.CurrentItem });

编译并运行得很好,但我没有绑定到正确的对象。

第一次显示UserControl时,我看到绑定到它的正确对象。但是当我关闭它,并将集合中的CurrentItem移动到另一个对象,并再次显示UserControl时,我仍然看到绑定的第一个对象。如果我再次关闭它,并再次打开它,那么我将看到绑定到控件的正确对象。

我已经检查了代码,并且每次绑定的CurrentItem都是正确的,但它似乎只是需要每隔一段时间。

所以我尝试明确清除绑定,首先:

BindingOperations.ClearBinding(userControlA, UserControlA.myProperty);
userControlA.SetBinding(UserControlA.myProperty, new Binding()
    { Source = this.viewModel.myCollection.CurrentItem });

但这似乎没有任何区别。

总而言之,感觉就像我正在跑兔子洞,越来越深入地追求复杂性,解决应该是一个相当简单的问题。

有没有人有任何建议:

  1. 如何绑定以处理动态添加的元素,或
  2. 如何在不使用动态排序元素的情况下,将任意排序的弹出窗口显示为影箱?
  3. 提前致谢。

1 个答案:

答案 0 :(得分:0)

虽然我无法创建新的Windows似乎很奇怪,但我绝对建议不要通过执行诸如将视图存储在MainWindow资源中等不必要的事情来使其复杂化。

如果你只是将这些元素的新实例添加到ObservableCollection中会更好:

XAML:

<Window x:Class="WpfApplication4.Window8"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication4"
        Title="Window8" Height="300" Width="300">
    <Window.Resources>
        <DataTemplate DataType="{x:Type local:ViewModel1}">
            <StackPanel Background="Green">
                <TextBlock Text="This is ViewModel1!!"/>
                <TextBlock Text="{Binding Text}"/>
            </StackPanel>
        </DataTemplate>

        <DataTemplate DataType="{x:Type local:ViewModel2}">
            <StackPanel Background="Blue" HorizontalAlignment="Center">
                <TextBlock Text="This is ViewModel2!!"/>
                <TextBlock Text="{Binding Text2}"/>

            </StackPanel>
        </DataTemplate>

        <DataTemplate DataType="{x:Type local:ViewModel3}">
            <StackPanel Background="Red" VerticalAlignment="Center">
                <TextBlock Text="This is ViewModel3!!"/>
                <TextBlock Text="{Binding Text3}"/>
                <TextBox Text="{Binding Text3}"/>
            </StackPanel>
        </DataTemplate>

    </Window.Resources>
    <DockPanel>
        <Button Width="100" Content="Add" Click="Add_Click" DockPanel.Dock="Top"/>
        <Button Width="100" Content="Remove" Click="Remove_Click" DockPanel.Dock="Top"/>

        <ListBox ItemsSource="{Binding ActiveWidgets}" SelectedItem="{Binding SelectedWidget}">
            <ListBox.Template>
                <ControlTemplate>
                    <ItemsPresenter/>
                </ControlTemplate>
            </ListBox.Template>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Grid IsItemsHost="True"/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ListBox.ItemContainerStyle>
                <Style TargetType="ListBoxItem">
                    <Setter Property="VerticalAlignment" Value="Stretch"/>
                    <Setter Property="HorizontalAlignment" Value="Stretch"/>
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="ListBoxItem">
                                <ContentPresenter ContentSource="Content"/>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListBox.ItemContainerStyle>
        </ListBox>
    </DockPanel>
</Window>

代码背后:

using System.Linq;
using System.Windows;
using System.Collections.ObjectModel;
using System;

namespace WpfApplication4
{
    public partial class Window8 : Window
    {
        private WidgetsViewModel Widgets { get; set; }

        public Window8()
        {
            InitializeComponent();

            DataContext = Widgets = new WidgetsViewModel();
        }

        private Random rnd = new Random();
        private int lastrandom;
        private void Add_Click(object sender, RoutedEventArgs e)
        {
            var random = rnd.Next(1, 4);
            while (random == lastrandom)
            {
                random = rnd.Next(1, 4);
            }

            lastrandom = random;

            switch (random)
            {
                case 1:
                    Widgets.ActiveWidgets.Add(new ViewModel1() {Text = "This is a Text"});
                    break;
                case 2:
                    Widgets.ActiveWidgets.Add(new ViewModel2() { Text2 = "This is another Text" });
                    break;
                case 3:
                    Widgets.ActiveWidgets.Add(new ViewModel3() { Text3 = "This is yet another Text" });
                    break;
            }

            Widgets.SelectedWidget = Widgets.ActiveWidgets.LastOrDefault();

        }

        private void Remove_Click(object sender, RoutedEventArgs e)
        {
            Widgets.ActiveWidgets.Remove(Widgets.SelectedWidget);
            Widgets.SelectedWidget = Widgets.ActiveWidgets.LastOrDefault();
        }
    }

    public class WidgetsViewModel: ViewModelBase
    {
        public ObservableCollection<ViewModelBase> ActiveWidgets { get; set; }

        private ViewModelBase _selectedWidget;
        public ViewModelBase SelectedWidget
        {
            get { return _selectedWidget; }
            set
            {
                _selectedWidget = value;
                NotifyPropertyChange(() => SelectedWidget);
            }
        }

        public WidgetsViewModel()
        {
            ActiveWidgets = new ObservableCollection<ViewModelBase>();
        }
    }

    public class ViewModel1: ViewModelBase
    {
        public string Text { get; set; }
    }

    public class ViewModel2: ViewModelBase
    {
        public string Text2 { get; set; }
    }

    public class ViewModel3: ViewModelBase
    {
        public string Text3 { get; set; }
    }
}

只需将我的代码复制并粘贴到文件 - 新建 - WPF应用程序中,然后自行查看结果。

由于Grid始终将最后一个UI元素添加到最顶层,因此您将看到向observablecollection添加项目会使这些“不同的小部件”始终显示在彼此之上,最顶层是添加的最后一个。< / p>

最重要的是,当WidgetA请求打开WidgetB时,只需创建一个新的WidgetBViewModel并将其添加到ActiveWidgets集合中。然后,当不再需要WidgetB时,只需将其删除即可。

然后,只需将UserControl放入适合每个ViewModel的DataTemplate中。我强烈建议您为每个窗口小部件保留一个单独的ViewModel,如果需要在它们之间共享数据,只需在ViewModel之间共享数据。 请勿尝试执行ListBox ItemsSource="{Binding Whatever, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}" 之类的操作,除非您有充分的理由。

这样您就不再需要处理Panel.ZIndex内容了。也许你可以创建一些附加的属性来处理焦点和诸如此类的东西,但这种方法很简单,而且比VisibilityResources方法更有效。