10

我对特定的 xaml 数据绑定有疑问。我有两个列表框(主详细信息,因此列表框的 IsSynchronizedWithCurrentItem 设置为 true)。我希望我的视图模型知道详细信息列表框上的选定项目何时更改:我在视图模型类上创建了一个 int 属性(即我们可以调用此属性 SelInd)并在详细信息视图模型上以这种方式绑定:

    SelectedIndex="{Binding Mode=OneWayToSource, Path=SelInd}"

我在运行时没有收到错误/异常,但绑定不会触发:当所选项目更改时,我的视图模型的属性不会更新。如果我将绑定模式更改为 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;}

    ....
}
4

2 回答 2

5

我认为在你的情况下,你必须使用 mode OneWay。默认情况下,您使用了 mode TwoWay

从MSDN引用关于TwoWay

TwoWay 绑定导致对源属性或目标属性的更改自动更新另一个。这种类型的绑定适用于可编辑的表单或其他完全交互的 UI 场景。大多数属性默认为 OneWay 绑定,但一些依赖属性(通常是用户可编辑控件的属性,例如 TextBox 的 Text 属性和 CheckBox 的 IsChecked 属性)默认为 TwoWay 绑定。确定依赖属性默认绑定单向还是双向的一种编程方法是使用 GetMetadata 获取属性的属性元数据,然后检查 BindsTwoWayByDefault 属性的布尔值。

模式OneWay,你need

OneWay 绑定导致对源属性的更改自动更新目标属性,但对目标属性的更改不会传播回源属性。如果被绑定的控件是隐式只读的,则这种类型的绑定是合适的。例如,您可以绑定到诸如股票代码之类的源,或者您的目标属性可能没有提供用于进行更改的控制接口,例如表格的数据绑定背景颜色。如果不需要监听目标属性的变化,使用 OneWay 绑定方式可以避免 TwoWay 绑定方式的开销。

模式OneWayToSource

OneWayToSource 是reverseOneWay 绑定的;它会在目标属性更改时更新源属性。一个示例场景是,如果您只需要从 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( ),该属性是绑定的参数。DataContextMySelectedIndexOneWayToSource

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将在视觉树workingGrid下方。因此,以下代码将起作用:

<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,会传递元素名称。

于 2013-08-08T05:10:04.283 回答
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 的绑定不起作用,我想知道它:)

于 2013-08-08T18:35:54.940 回答