对于子ViewModel属性,PropertyChanged值为null

时间:2016-06-25 18:30:19

标签: wpf mvvm datacontext inotifypropertychanged

我的主ViewModel中有一个ViewModel的ObservableCollection。绑定似乎工作正常,因为我可以切换视图。但是,在ObservableCollection的元素中引发ViewModelBase OnPropertyChanged方法(适用于其他内容)会导致ViewModelBase中的null PropertyChanged值。

这是我的主要代码段:

在我的主ViewModel 构造函数中:

    public EditorViewModel()
    {
        base.DisplayName = Strings.EditorName;

        _availableEditors = new ObservableCollection<ViewModelBase>();

        AvailableEditors.Add(new GBARomViewModel(646, 384));
        AvailableEditors.Add(new MonsterViewModel(800, 500));

        CurrentEditor = _availableEditors[0];
    }

在GBA ROM加载中,ViewModel和Model属性已更新:

    void RequestOpenRom()
    {
        OpenFileDialog dlg = new OpenFileDialog();
        dlg.DefaultExt = ".gba";
        dlg.Filter = "GBA ROM (.gba)|*.gba|All files (*.*)|*.*";
        dlg.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

        Nullable<bool> result = dlg.ShowDialog();

        if (result == true)
        {
            if(CurrentEditor is GBARomViewModel)
            {
                (CurrentEditor as GBARomViewModel).ReadRom(dlg.FileName);
            }               
        }
    }

在我的主视图中: TabControl的变体(用于视图切换和视图状态保存)。

    <controls:TabControlEx ItemsSource="{Binding AvailableEditors}"
        SelectedItem="{Binding CurrentEditor}"
        Style="{StaticResource BlankTabControlTemplate}"
        MinWidth="{Binding CurrentEditorWidth}"
        MinHeight="{Binding CurrentEditorHeight}"
        MaxWidth="{Binding CurrentEditorWidth}"
        MaxHeight="{Binding CurrentEditorHeight}"
        Width="{Binding CurrentEditorWidth}"
        Height="{Binding CurrentEditorHeight}"
        HorizontalAlignment="Left"
        VerticalAlignment="Top">
        <controls:TabControlEx.Resources>
            <DataTemplate DataType="{x:Type vm:GBARomViewModel}">
                <vw:GBARomView />
            </DataTemplate>
            <DataTemplate DataType="{x:Type vm:MonsterViewModel}">
                <vw:MonsterView />
            </DataTemplate>
        </controls:TabControlEx.Resources>
    </controls:TabControlEx>

在GBARomViewModel (子ViewModel,AvailableEditors的元素)

    public String CRC32
    {
        get
        {
            return _rom.CRC32;
        }
        set
        {
            if (value.Equals(_rom.CRC32))
            {
                return;
            }

            _rom.CRC32 = value;
            OnPropertyChanged("CRC32");
        }
    }

子视图中的属性绑定
现在这是一个UserControl,所以我也会把它的代码放在后面。启动时的其他属性,如LabelWidth和LabelValue。在XAML中为TextBoxValue提供默认值也可以。

    <StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="10, 0, 0, 10" Width="300">
        <dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomTitle}" TextBoxValue="{Binding Title}" />
        <dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomGameCode}" TextBoxValue="{Binding GameCode}" />
        <dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomRomSize}" TextBoxValue="{Binding RomSize}"  />
        <dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="100" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomCRC32}" TextBoxValue="{Binding CRC32}"  />
        <dlb:DefaultLabelBox LabelWidth="82" TextBoxWidth="200" HorizontalAlignment="Left" LabelValue="{x:Static p:Strings.RomMD5Checksum}" TextBoxValue="{Binding MD5Checksum}"/>
    </StackPanel>

DefaultLabelBox.cs

<UserControl x:Name="uc">
    <StackPanel>
        <TextBlock Text="{Binding Path=LabelValue, ElementName=uc}"
           Width="{Binding Path=LabelWidth, ElementName=uc}"/>
        <Label Content="{Binding Path=TextBoxValue, Mode=OneWay, ElementName=uc}"
         Width="{Binding Path=TextBoxWidth, ElementName=uc}"/>
    </StackPanel>
</UserControl>

DefaultLabelBox.xaml.cs

    public string TextBoxValue
    {
        get {
            return (string)GetValue(TextBoxValueProperty);
        }
        set {
            SetValue(TextBoxValueProperty, value);
        }
    }

    public static readonly DependencyProperty TextBoxValueProperty =
        DependencyProperty.Register("TextBoxValue", typeof(string), typeof(DefaultLabelBox), new PropertyMetadata(default(string)));

控制模板

<Style TargetType="dlb:DefaultLabelBox">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="dlb:DefaultLabelBox">
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding LabelValue, RelativeSource={RelativeSource TemplatedParent}}"
                               MinWidth="20"
                               Width="{Binding LabelWidth, RelativeSource={RelativeSource TemplatedParent}}"
                               VerticalAlignment="Center"
                               FontFamily="Mangal"
                               Height="20"
                               FontSize="13"/>
                    <Label Content="{Binding TextBoxValue, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}"
                           BorderBrush="{StaticResource DefaultLabelBoxBorderBrush}"
                           BorderThickness="1"
                           Padding="1,1,1,1"
                           Background="{StaticResource DefaultLabelBoxBackgroundBrush}"
                           Foreground="{StaticResource DefaultLabelBoxForeground}"
                               MinWidth="60"
                               Height="20"
                               VerticalAlignment="Center"
                               FontFamily="Mangal"
                               Width="{Binding TextBoxWidth, RelativeSource={RelativeSource TemplatedParent}}" 
                               FontSize="13"/>
                </StackPanel>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

我尝试了一些事情但是对MVVM不熟悉我不知道我是否有一个绑定的DataContext问题。任何帮助将不胜感激。

编辑:我对一些代码进行了更改,以便为我说明工作解决方案以及添加我忘记的ControlTemplate。我不确定在UserControl和ControlTemplate中是否强制使用Mode = OneWay,但它现在正在工作,所以我将其保留原样。

1 个答案:

答案 0 :(得分:1)

为了制作像

这样的装订
<dlb:DefaultLabelBox ... TextBoxValue="{Binding CRC32, Mode=TwoWay}" />

工作,DefaultLabelBox需要从其父控件继承其DataContext(这是顺便说一句。这是UserControl永远不应该显式设置其DataContext的原因。)

然而,&#34;内部&#34;然后,UserControl的XAML中的绑定需要一个显式指定的Source或RelativeSource或ElementName。

所以他们应该(例如)看起来像这样:

<UserControl ... x:Name="uc">
    <StackPanel>
        <TextBlock
             Text="{Binding Path=LabelValue, ElementName=uc}"
             Width="{Binding Path=LabelWidth, ElementName=uc}"/>
        <TextBox
            Text="{Binding Path=TextBoxValue, Mode=TwoWay, ElementName=uc}"
            Width="{Binding Path=TextBoxWidth, ElementName=uc}"/>
    </StackPanel>
</UserControl>