我有一个wpf应用程序,它有一个主窗口和菜单 . 此主窗口有一个面板,在单击菜单项时,我创建一个用户控件的实例,并使用控件加载面板 .
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="" MinHeight="750" Height="Auto" MinWidth="1100" Width="Auto" WindowState="Maximized" ScrollViewer.VerticalScrollBarVisibility="Auto"
Loaded ="MainWindow_OnLoaded" Closing="Window_Closing">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility ="Auto" SizeChanged="ScrollViewer_SizeChanged">
<Grid Width="Auto">
<Grid.RowDefinitions>
<RowDefinition Height="38"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel Height="38" Width="Auto" Background="#09527B">
<Grid Margin="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="70"></ColumnDefinition>
</Grid.ColumnDefinitions>
</Grid>
</StackPanel>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="189"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<StackPanel>
<Expander Name="test" Header="Admin" Foreground="White" Margin="0,10,0,0">
<StackPanel Margin="20,0,0,0">
<Expander Header="Data" Foreground="White">
<StackPanel>
<TextBlock Text="Add/Edit UC1" Foreground="White" Margin="30,5,0,0" MouseDown="OpenUC1_MouseDown" MouseEnter="TextBlock_MouseEnter" MouseLeave="TextBlock_MouseLeave"/>
<TextBlock Text="Add/Edit UC2" Name="tbxBuild" Foreground="White" Margin="30,5,0,0" MouseDown="OpenUC2_MouseDown" MouseEnter="TextBlock_MouseEnter" MouseLeave="TextBlock_MouseLeave"/>
</StackPanel>
</Expander>
</StackPanel>
</Grid>
<StackPanel Grid.Column="1">
<Grid Name="pnlMain" Height ="Auto" VerticalAlignment="Top" HorizontalAlignment="Left">
</Grid>
</StackPanel>
</Grid>
</Grid>
</ScrollViewer>
</Window>
MainWindow.cs
private void OpenUC1_MouseDown(object sender, MouseButtonEventArgs e)
{
for (int i = 0; i < pnlMain.Children.Count; i++ )
{
pnlMain.Children.Remove(pnlMain.Children[i]);
}
using (UC2 _uc2= new UC2())
{
pnlMain.Children.Add(_uc2);
}
}
private void OpenUC2_MouseDown(object sender, MouseButtonEventArgs e)
{
for (int i = 0; i < pnlMain.Children.Count; i++ )
{
pnlMain.Children.Remove(pnlMain.Children[i]);
}
using (UC1 _uc1= new UC1())
{
pnlMain.Children.Add(_uc1);
}
}
我的问题是当我从主面板上移除控件(UC1)时,该控件何时被丢弃?用户控件(UC1和UC2)都具有附加到其数据上下文的相同视图模型 . 所以我发现删除用户控件(UC1)中的一些方法即使从面板中删除也会被调用 . 原因是,当创建新的UC2实例时,数据模型中存在一些实际上调用UC1中的依赖方法的变化 . 但如果UC1被处理掉,这种情况就不会发生 . 如何在创建UC2实例之前确定处理UC1?
public UC1()
{
InitializeComponent();
this.DataContext = App.ViewModel.TestViewModel;
}
private void UC1_Unloaded(object sender, RoutedEventArgs e)
{
this.DataContext = null;
}
public UC2()
{
InitializeComponent();
this.DataContext = App.ViewModel.TestViewModel;
}
private void UC2_Unloaded(object sender, RoutedEventArgs e)
{
this.DataContext = null;
}
从面板中删除控件时,不会立即调用卸载的方法 .
2 回答
当我编写并测试代码以从窗口的可视化树中动态添加和删除
UserControl
对象时,我发现Unloaded
事件是按预期引发的 .在您自己的代码示例中,至少存在一个严重问题,以及两个不一致:
严重的问题是如何移除儿童 . 您的
for
循环通过索引遍历pnlMain
对象的子项(Grid
) . But removing any child invalidates the sequence of indexes! 也就是说,循环将首先删除索引0处的子节点;这会导致索引1处的子节点现在成为索引0处的子节点 . 但循环在继续之前递增索引,然后将删除索引1处的子节点 . 此子节点最初位于索引 2 处 . 代码会跳过其他所有子代(即最初在奇数编号索引处的代码),其中一半作为Grid
的子代附加 .Incongruity#1:我希望在名称中使用短语"OpenUC1"的方法添加
UC1
的实例 . 但是,您的OpenUC1_MouseDown()
方法似乎正在添加UC2
的实例(反之亦然OpenUC2_MouseDown()
) . 至少,代码中应该有一个注释,解释为什么代码与给定方法名称的预期不同 .Incongruity#2:在添加
UserControl
对象时,对Add()
的调用有一个using
语句 . 首先,UserControl
本身没有实现IDisposable
,所以除非您的类型已经实现了该接口,否则该代码甚至不合法 . 其次,即使你的UserControl
子类确实实现了那个接口,对我来说,处理你刚刚创建的对象以及你在可视化树中保留的对象(例如通过将它添加到Grid
中)似乎不是一个好主意 . 的孩子们) .不幸的是,正如我在评论中提到的那样,如果没有a good, minimal, complete code example可靠地再现您的问题,就不可能说出为什么您的代码不会像人们希望和/或期望的那样表现 . 上述任何一点(尤其是#1)都可能是您所看到的行为的原因,但我无法确切知道 .
如果在解决了这些问题之后(或者以某种方式确定它们不是问题......虽然如果你能合法地做到这一点,我会认为代码仍然存在缺陷,从某种意义上讲它的设计很差),你会发现你的问题仍然存在,请编辑您的问题,以便它包含一个可靠,最简单,完整的代码示例,可以可靠地重现问题 .
与此同时,这是一个简单的代码示例,它说明了从可视树中删除对象时正如所期望的那样引发
Unloaded
事件的基本行为 . 请注意,虽然从Grid
对象的Children
集合中删除所有子项的正确方法是简单地调用Clear()
方法(例如pnlMain.Children.Clear()
),但我已经包含了一个可行的显式循环方法示例 .XAML:
UserControl1.xaml
MainWindow.xaml
C#:
UserControl1.xaml.cs
MainWindow.xaml.cs
关于 Loaded/Unloaded 事件引用MSDN forum entry:
所以答案是你无法预测这些事件的确切时间会被提出,你不应该期望在你从父母那里删除一个控件后立即调用它们 .
它是一个快速而肮脏的解决方案:而不是确保给定的用户控制' events are fired in time let'在运行方法之前检查UC1 / UC2对象的 Parent property . 如果属性为null,则删除UC1 / UC2对象,不应执行该方法 .
但是,让我指出这段代码的一些问题:
什么's the point of the using block in the MouseDown event handlers? You create a user control object, add it to the panel and then immediately after that you call the Dispose method on it? (that'什么是使用块在C#中做什么
您不需要for循环来从Panel控件中删除所有子元素,如Grid . 你可以在一行中做到这一点 .
pnlMain.Children.Clear();