【问题标题】:ListBox not correctly changing ContentControl's ViewModelListBox 未正确更改 ContentControl 的 ViewModel
【发布时间】:2013-08-20 05:45:27
【问题描述】:

我对 WPF 还是很陌生,我决定更改我正在开发的应用程序,以尽可能地遵循 MVVM 模式。当我尝试让列表框指示内容控件的视图模型时,我遇到了问题。我已经被这个问题困扰了一段时间,在互联网上搜索并没有为我找到答案。

由于某种原因,列表框包含的视图模型的新实例正在生成为内容控件的数据上下文。当我在调试时,我确保列表框包含它应该包含的视图模型,并且我在列表框中选择的项目确实是列表框正在选择的项目,但是内容控件会根据选择而变化。有一个视图模型填充内容控件,但它不在列表框填充的集合中。我可以通过我的删除按钮以某种方式删除内容控件中的视图模型。但是,当我在列表框上进行选择更改或将新项目添加到集合时,它会使用再次不在集合中的新视图模型填充内容控件。我不知道它为什么这样做,或者我的代码中的什么暗示了这种行为。

我做了一个简单的应用程序来尝试找出我做错了什么。它完美地复制了我的问题。我很确定按钮不遵守 MVVVM(应该运行包含在视图模型中的命令以遵守我一直在阅读的 MVVM)但这不是我现在主要关心的问题,因为没有按钮。

MainWindow.xml

<Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:WpfApplication1" x:Class="WpfApplication1.MainWindow"
        Title="MainWindow" Height="440" Width="436">
    <Window.DataContext>
        <local:mwvm/>
    </Window.DataContext>
    <Window.Resources>
        <DataTemplate DataType="{x:Type local:ucvm}">
            <local:uc/>
        </DataTemplate>
    </Window.Resources>
    <Grid>
        <Button Content="a" HorizontalAlignment="Left" Margin="323,351,0,0" VerticalAlignment="Top" Width="95" Click="Button_Click"/>
        <Button Content="r" HorizontalAlignment="Left" Margin="323,378,0,0" VerticalAlignment="Top" Width="95" Click="Button_Click_1"/>
        <ContentControl Margin="10,10,110,10" Content="{Binding SelectedItem, ElementName=lb_UCs}"/>
        <ListBox x:Name="lb_UCs" HorizontalAlignment="Left" Height="336" Margin="323,10,0,0" VerticalAlignment="Top" Width="95" ItemsSource="{Binding UCs}" DisplayMemberPath="CoolText"/>
    </Grid>
</Window>

MainWindow.xaml.cs

public partial class PanelPartsView : UserControl
{
    private PanelPartsViewModel _DC;

    public PanelPartsView()
    {
        InitializeComponent();
        _DC = DataContext as PanelPartsViewModel;
    }

    private void btn_Remove_Click(object sender, RoutedEventArgs e)
    {
        _DC.Panels.Remove(lb_Panels.SelectedItem as PartsViewModel);
    }

    private void btn_Add_Click(object sender, RoutedEventArgs e)
    {
        var pvm = new PartsViewModel();
        _DC.Panels.Add(pvm);
        lb_Panels.SelectedItem = pvm;
        System.Console.WriteLine("lb_Panels.selecteditem = {0}", ((PartsViewModel)lb_Panels.SelectedItem).PanelName);
        System.Console.WriteLine("cc_PanelParts.content = {0}", ((PartsViewModel)cc_PanelParts.Content).PanelName);
    }
}

mwvm

class mwvm
{
    private ObservableCollection<ucvm> _UCs = new ObservableCollection<ucvm>();

    public ObservableCollection<ucvm> UCs
    {
        get { return _UCs; }
    }

    public mwvm()
    {
        //this is for for testing, the real application would be purely dynamic
        _UCs.Add(new ucvm());
        _UCs.Add(new ucvm());
        _UCs.Add(new ucvm());
    }
}

uc.xaml

<UserControl
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfApplication1" x:Class="WpfApplication1.uc" 
             mc:Ignorable="d" d:DesignWidth="300" Height="90">
    <Grid>
        <Grid.DataContext>
            <local:ucvm/>
        </Grid.DataContext>
        <Button Content="{Binding CoolText}" Margin="10,10,10,0" Height="44" VerticalAlignment="Top"/>
        <TextBox Height="23" Margin="10,59,10,0" TextWrapping="Wrap" Text="{Binding CoolText}" VerticalAlignment="Top"/>
    </Grid>
</UserControl>

uc.xaml.cs

public partial class uc : UserControl
{
    public uc()
    {
        InitializeComponent();
    }
}

ucvm.cs

class ucvm : INotifyPropertyChanged
{
    private static int i = 1;

    private string _CoolText = "<" + i++ + ">" + System.DateTime.Now.ToLongTimeString();
    public string CoolText
    {
        get { return _CoolText; }
        set
        {
            _CoolText = value;
            NPC("CoolText");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    private void NPC(string s)
    {
        if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(s));
    }
}

我也尝试过像这样绑定内容控件...

<ContentControl Content="{Binding SelectedUCVMl, Mode=OneWay}"/>
<ListBox x:Name="lb_UCs" ItemsSource="{Binding UCs}" SelectedItem="{Binding SelectedUCVM}" DisplayMemberPath="CoolText"/>

...等等...

<ContentControl Content="{Binding UCs/}"/>
<ListBox x:Name="lb_UCs" ItemsSource="{Binding UCs}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="CoolText"/>

但无济于事。

任何帮助将不胜感激。

【问题讨论】:

    标签: c# wpf xaml data-binding mvvm


    【解决方案1】:

    看起来您只需从 uc.xaml 中删除这部分:

        <Grid.DataContext>
            <local:ucvm/>
        </Grid.DataContext>
    

    这种语法会在每次创建 uc.xaml 实例时创建一个新的视图模型实例,这当然不是您想要的。您希望 uc.xaml 实例的数据上下文继承当前在列表框中选择的实例。

    【讨论】:

    • 所以为了确保我理解发生了什么,首先正确分配了数据上下文,然后调用了 uc 的构造函数(显示 ucvm 的视图)中的 InitiallizeComponent() 方法,并且它将继承的数据上下文覆盖为 ucvm 的新实例?
    • @Orilay 是的,完全正确。 XAML 的 sn-p 等同于在 InitializeComponent() 之后有 this.DataContext = new ucvm()
    • 啊,我明白了。非常感谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    相关资源
    最近更新 更多