我有一个ObservableCollection的ViewModels在我的主ViewModel。绑定似乎工作得很好,因为我可以切换视图。然而,在ObservableCollection的元素中引发ViewModelBase OnPropertyChanged方法(该方法适用于其他东西)会导致ViewModelBase中的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>
In 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问题。如有任何帮助,不胜感激。
Edit:我对一些代码进行了更改,以说明为我提供的工作解决方案以及添加我忘记的ControlTemplate。我不确定Mode=OneWay是否在UserControl和ControlTemplate中是强制性的,但它现在正在工作,所以我让它保持原样
为了创建一个像
这样的绑定<dlb:DefaultLabelBox ... TextBoxValue="{Binding CRC32, Mode=TwoWay}" />
工作时,DefaultLabelBox需要从它的父控件继承它的DataContext(这是btw。这就是为什么UserControl不应该显式地设置它的DataContext的原因。
然而,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>