C# WPF如何注销附加的行为
我正在用WPF写一份申请书。我有一个类似于Sheridan在中描述的设置 具体地说,我有一个listview,它有一个select命令,将我的RootViewModel上的Mainview属性设置为所选条目的属性。它显示在以下XAML中C# WPF如何注销附加的行为,c#,wpf,xaml,mvvm,C#,Wpf,Xaml,Mvvm,我正在用WPF写一份申请书。我有一个类似于Sheridan在中描述的设置 具体地说,我有一个listview,它有一个select命令,将我的RootViewModel上的Mainview属性设置为所选条目的属性。它显示在以下XAML中 <DockPanel> <UserControl DockPanel.Dock="Top" Content="{Binding MenuView,Mode=OneWay}"/> <Border DockPanel.D
<DockPanel>
<UserControl DockPanel.Dock="Top" Content="{Binding MenuView,Mode=OneWay}"/>
<Border DockPanel.Dock="Top" BorderThickness="0 2 0 0" BorderBrush="{StaticResource GarminDarkBrush}" Panel.ZIndex="1001">
<DockPanel LastChildFill="True">
<UserControl DockPanel.Dock="Left" Content="{Binding ExpandableLeftPanel,Mode=OneWay}" />
<UserControl Content="{Binding MainView,Mode=OneWay}" Panel.ZIndex="0" />
</DockPanel>
</Border>
</DockPanel>
这适用于显示不同的条目
现在来谈谈我的问题。在我显示的每个viewmodels中,都有一个子CONSOLUECOWERVIEWMODEL,该子CONSOLUECOWERVIEWMODEL具有一个CONSOLUEVIEWMODEL,在显示时将附加的行为注册到RichTextBox。当我选择列表中的每个项目时,将显示正确的视图,并且ConsoleViewModel将正确注册和运行。但是,当我在视图之间切换时,行为永远不会取消注册。这会导致一个问题,因为每当我切换视图时,都会创建一个新的RichTextBox(由WPF而不是我创建)并绑定到我的行为。这通常会导致内存泄漏,因为我将事件处理程序附加到RTB。我通过使用ConsoleViewModel字典并通过ConsoleMediator类交换与ConsoleViewModel关联的RTB修复了此内存泄漏
我仍然希望了解如何正确地注销RTB,以便在视图之间交换时,可以注销ConsoleViewModel上的其他事件处理程序,这会浪费CPU更改未显示的RTB,并且无论如何查看时都会被替换
ConsoleControlServiceWModel的一部分
public class ConsoleControllerViewModel : ViewModelBase
{
public virtual ConsoleViewModel Console { get; set; }
}
下面是附加的行为
public static class ConsoleTextBoxBehavior
{
public static readonly DependencyProperty ConsoleBindingProperty =
DependencyProperty.RegisterAttached("Console",
typeof(ConsoleViewModel), typeof(ConsoleTextBoxBehavior),
new UIPropertyMetadata(null, OnConsoleBound));
public static ConsoleViewModel GetConsole(DependencyObject obj)
{
return (ConsoleViewModel)obj.GetValue(ConsoleBindingProperty);
}
public static void SetConsole(DependencyObject obj, ConsoleViewModel value)
{
obj.SetValue(ConsoleBindingProperty, value);
}
private readonly static Dictionary<ConsoleViewModel, ConsoleMediator> _registeredConsoles =
new Dictionary<ConsoleViewModel, ConsoleMediator>();
private static void OnConsoleBound(object sender, DependencyPropertyChangedEventArgs e)
{
var textBox = sender as RichTextBox;
if(textBox == null)
{
throw new ArgumentException("Can only bind to RichTextBoxes");
}
var c = e.OldValue as ConsoleViewModel;
if (e.OldValue != null && _registeredConsoles.ContainsKey(c))
{
// never hit
var cm = _registeredConsoles[c];
cm.UnhookConsole();
_registeredConsoles.Remove(c);
}
c = e.NewValue as ConsoleViewModel;
if (c != null)
{
HookUpConsole(textBox, c);
}
}
//other functions left out for brevity
}
公共静态类ConsoleTextBoxBehavior
{
公共静态只读从属属性ConsoleBindingProperty=
DependencyProperty.RegisterAttached(“控制台”,
typeof(控制台视图模型)、typeof(控制台ExtBoxBehavior),
新的UIPropertyMetadata(null,OnConsolleBound));
公共静态控制台ViewModel GetConsole(DependencyObject obj)
{
return(ConsoleViewModel)对象GetValue(ConsoleBindingProperty);
}
公共静态void SetConsole(DependencyObject对象、ConsoleViewModel值)
{
对象设置值(ConsoleBinding属性,值);
}
专用只读静态字典\u注册的解决方案=
新字典();
ConsolleBound上的私有静态无效(对象发送方,DependencyPropertyChangedEventArgs e)
{
var textBox=发送方为RichTextBox;
if(textBox==null)
{
抛出新ArgumentException(“只能绑定到RichTextBox”);
}
var c=e.OldValue作为控制台视图模型;
if(e.OldValue!=null&&u registeredConsoles.ContainsKey(c))
{
//从不打
var cm=_注册的解决方案[c];
cm.UnhookConsole();
_已注册的解决方案。删除(c);
}
c=e.作为控制台视图模型的新值;
如果(c!=null)
{
连接控制台(文本框,c);
}
}
//为简洁起见,省略了其他函数
}
这里它被连接到RTB。请注意,{Binding.}之所以有效,是因为它来自于ConsoleView模型的上下文,而不是ConsoleController服务模型
<RichTextBox Name="ConsoleOutput"
ext:ConsoleTextBoxBehavior.Console="{Binding .}"
IsReadOnly="True"
>
我理解,当通知特定属性发生更改时,应调用OnConsoleBound。在我的特定应用程序中,我从不需要该ConsoleView模型相对于其父级进行更改。那么,我是否必须修改viewmodel以侦听rootview上的事件,以确定它是否是实际显示的事件?然后将Viewmodel切换到临时位置,然后在显示时将其放回原位?看起来很难看,所以我想在弄得一团糟之前向花生画廊寻求另一个解决方案
此外,ViewModelBase是一个INotifyPropertyChanged,我有一些魔术(动态代理)来为虚拟成员引发这些事件。因此,请不要因为没有看到正在引发的属性更改事件而挂断电话
<RichTextBox Name="ConsoleOutput"
ext:ConsoleTextBoxBehavior.Console="{Binding .}"
IsReadOnly="True"
>