我遇到了特定的xaml数据绑定问题。 我有两个列表框(主 - 详细信息,因此列表框将IsSynchronizedWithCurrentItem设置为true)。我希望我的viewmodel知道详细信息列表框中所选项目何时发生更改:我在viewmodel类上创建了一个int属性(即我们可以调用此属性SelInd),并在详细视图模型上以这种方式绑定:
SelectedIndex="{Binding Mode=OneWayToSource, Path=SelInd}"
我在运行时没有得到任何错误/异常,但绑定没有触发:当所选项目发生更改时,我的viewmodel属性不会更新。如果我将绑定模式更改为TwoWay,一切正常,但这不是我需要的。我需要它与OneWayToSource一起使用(顺便说一句,如果我将SelectedItem绑定到SelectedValue属性,则适用相同的非工作行为)。
为什么这些绑定不会触发OneWayToSource?
这是一个更完整的代码示例,只是为了让事情变得更清晰: 编辑:我无法显示真实代码(NDA),但我会在这里展示更简单和类似的东西(Page的DataContext是稍后解释的PageViewModel类的实例) 我只需要我的viewmodel类的SelInd属性应该始终在第二个ListBox中反映SelectedIndex的值。我已经找到了这样做的替代方法(代码隐藏中的事件处理程序或附加行为)但是现在我只是好奇它为什么它不适用于OneWayToSource绑定。
<Page>
<ContentControl x:Name="MainDataContext">
<Grid DataContext={Binding Path=Masters}>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0"
SelectionMode="Single"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding }">
<ListBox.ItemContainerStyle>
...
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
....
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Grid.Column="1"
SelectionMode="Single"
SelectedIndex="{Binding Mode=OneWayToSource, ElementName=MainDataContext,Path=DataContext.SelInd}"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Details}">
<ListBox.ItemContainerStyle>
...
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
....
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</ContentControl>
</Page>
这是视图模型类的草图
public class PageViewModel{
public ObservableCollection<MasterClass> Masters {get;set;}
public int SelInd {get;set;}
....
}
这里是MasterClass,它只是一个名字和一个细节列表
public class MasterClass{
public ObservableCollection<DetailsClass> Details {get;set;}
public String MasterName {get;set;}
....
}
答案 0 :(得分:5)
我认为在您的情况下,您必须使用模式OneWay
。默认情况下,您使用的是模式TwoWay
。
引自MSDN关于TwoWay
:
TwoWay绑定会导致更改源属性或目标属性以自动更新另一个属性。此类绑定适用于可编辑表单或其他完全交互式UI方案。大多数属性默认为OneWay绑定,但某些依赖项属性(通常是用户可编辑控件的属性,如TextBox的Text属性和CheckBox的IsChecked属性)默认为TwoWay绑定。确定依赖项属性是默认绑定单向还是双向的一种编程方法是使用GetMetadata获取属性的属性元数据,然后检查BindsTwoWayByDefault属性的布尔值。
模式OneWay
,即 need
:
OneWay绑定会导致更改源属性以自动更新目标属性,但对目标属性的更改不会传播回源属性。如果绑定的控件是隐式只读的,则这种类型的绑定是合适的。例如,您可能绑定到股票代码等来源,或者您的目标属性没有提供用于进行更改的控制界面,例如表格的数据绑定背景颜色。如果不需要监视目标属性的更改,则使用OneWay绑定模式可以避免TwoWay绑定模式的开销。
模式OneWayToSource
:
OneWayToSource是OneWay绑定的
reverse
;它在目标属性更改时更新源属性。一个示例场景是,您只需要从UI重新评估源值。
下面的图表是为了更好地理解:
好的,然后我会告诉你一个适合我的例子。也许它会对你有用。
XAML
<Window x:Class="SelectedIndexHelp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SelectedIndexHelp"
Title="MainWindow" Height="350" Width="525"
ContentRendered="Window_ContentRendered"
WindowStartupLocation="CenterScreen">
<Window.Resources>
<local:SelectedIndexClass x:Key="SelectedIndexClass" />
</Window.Resources>
<Grid DataContext="{StaticResource SelectedIndexClass}">
<ListBox x:Name="MyListBox"
BorderThickness="1"
Width="200" Height="200"
BorderBrush="#CE5E48"
DisplayMemberPath="Name"
Background="AliceBlue"
SelectedIndex="{Binding MySelectedIndex, Mode=OneWayToSource}" />
<Label Name="SelectedIndex" VerticalAlignment="Top"
Content="{Binding MySelectedIndex}"
ContentStringFormat="SelectedIndex: {0}"
Width="100" Height="30" Background="Lavender" />
</Grid>
</Window>
Code behind
public partial class MainWindow : Window
{
public class Person
{
public string Name
{
get;
set;
}
public int Age
{
get;
set;
}
}
private ObservableCollection<Person> DataForListBox = new ObservableCollection<Person>();
public MainWindow()
{
InitializeComponent();
}
private void Window_ContentRendered(object sender, EventArgs e)
{
DataForListBox.Add(new Person()
{
Name = "Sam",
Age = 22,
});
DataForListBox.Add(new Person()
{
Name = "Nick",
Age = 21,
});
DataForListBox.Add(new Person()
{
Name = "Cris",
Age = 25,
});
DataForListBox.Add(new Person()
{
Name = "Josh",
Age = 36,
});
DataForListBox.Add(new Person()
{
Name = "Max",
Age = 32,
});
DataForListBox.Add(new Person()
{
Name = "John",
Age = 40,
});
MyListBox.ItemsSource = DataForListBox;
MyListBox.Focus();
}
}
public class SelectedIndexClass
{
private int? mySelectedIndex = 0;
public int? MySelectedIndex
{
get
{
return mySelectedIndex;
}
set
{
mySelectedIndex = value;
}
}
}
Output
在此示例中,有一类数据 - Person
,ListBox
的这些数据。类 SelectedIndexClass
(DataContext
),其中包含属性MySelectedIndex
,它是绑定OneWayToSource
的参数。
Edit:
我很高兴你能解决这个问题。我将尝试通过他们的例子来解释,为什么你不使用ElementName
案例。
所以,假设我们有这个代码:
<ContentControl x:Name="MainDataContext">
<Grid x:Name="MainGrid" DataContext="{StaticResource SelectedIndexClass}">
<ListBox x:Name="MyListBox"
BorderThickness="1"
Width="200" Height="200"
BorderBrush="#CE5E48"
DisplayMemberPath="Name"
Background="AliceBlue"
SelectedIndex="{Binding Path=DataContext.MySelectedIndex, Mode=OneWayToSource, ElementName=MainDataContext}" />
<Label Name="SelectedIndex" VerticalAlignment="Top"
Content="{Binding MySelectedIndex}"
ContentStringFormat="SelectedIndex: {0}"
Width="100" Height="30" Background="Lavender" />
</Grid>
</ContentControl>
正如您可能理解的那样,它不起作用。
DataContext
设置在可视化树的特定节点上,下面的所有项目(在可视树中)都会继承它。这意味着自DataContext
以及可见树下方working
将 Grid
。因此,以下代码将起作用:
<ContentControl x:Name="MainDataContext">
<Grid x:Name="MainGrid" DataContext="{StaticResource SelectedIndexClass}">
<ListBox x:Name="MyListBox"
BorderThickness="1"
Width="200" Height="200"
BorderBrush="#CE5E48"
DisplayMemberPath="Name"
Background="AliceBlue"
SelectedIndex="{Binding Path=DataContext.MySelectedIndex, Mode=OneWayToSource, ElementName=MainGrid}" />
<Label Name="SelectedIndex" VerticalAlignment="Top"
Content="{Binding MySelectedIndex}"
ContentStringFormat="SelectedIndex: {0}"
Width="100" Height="30" Background="Lavender" />
</Grid>
</ContentControl>
而且,如果点MyListBox
的名称,它将起作用。通常,在设置DataContext
时,会传递元素名称。
答案 1 :(得分:2)
好吧,我找到了一种让它发挥作用的方法。我刚删除了数据上下文“间接”,所以我不必在绑定中使用ElementName,它开始工作。工作的xaml示例是:
<Page>
<ContentControl >
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0"
SelectionMode="Single"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Masters }">
<ListBox.ItemContainerStyle>
...
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
....
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<ListBox Grid.Column="1"
SelectionMode="Single"
SelectedIndex="{Binding Mode=OneWayToSource, Path=SelInd}"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Masters/Details}">
<ListBox.ItemContainerStyle>
...
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
....
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</ContentControl>
</Page>
现在,如果有人确切知道为什么使用ElementName的绑定不起作用,我想知道它:)