0

TPTB 已决定我们的应用程序必须在单个窗口中运行,不允许以模态模式弹出新窗口。

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

所以我在窗口中添加了一个顶级网格。在那个网格中,我没有定义行或列,所以所有内容都绘制在第 0 行/第 0 列中。

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

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

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

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

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

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

这几乎奏效了。但是绑定都搞砸了。

例如,其中一个控件应该将属性绑定到 Window 视图模型的成员对象中的集合的 CurrentItem。当我将控件定义为网格中的不可见项时,它工作正常。但是当我将它定义为资源时,属性为空——它从未被绑定。

因此,在将其添加到网格后,我尝试将其绑定到代码中:

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. 如何在不使用动态排序元素的情况下将任意排序的弹出窗口显示为阴影框?

提前致谢。

4

1 回答 1

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 添加项目会使这些“不同的小部件”始终彼此重叠出现,最顶部是最后添加的一个。

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

然后,只需将您的 UserControls 放入每个 ViewModel 的适当 DataTemplate 中。我强烈建议您为每个 Widget 保留一个单独的 ViewModel,如果您需要在它们之间共享数据,只需在 ViewModel 之间共享数据即可。ListBox ItemsSource="{Binding Whatever, RelativeSource={RelativeSource FindAncestor, AncestorType=Window}" 除非你有充分的理由,否则不要尝试做类似的事情。

这样你就不再需要处理Panel.ZIndex东西了。也许您可以创建几个附加属性来处理诸如焦点之类的事情,但是这种方法非常简单,并且比 theVisibilityResources方法具有更高的性能。

于 2013-02-15T22:06:35.037 回答