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 });
但这似乎没有任何区别。
总而言之,感觉就像我正在跑兔子洞,越来越深入地追求复杂性,解决应该是一个相当简单的问题。
有没有人有任何建议:
提前致谢。
答案 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
内容了。也许你可以创建一些附加的属性来处理焦点和诸如此类的东西,但这种方法很简单,而且比Visibility
和Resources
方法更有效。