【问题标题】:Update Bound Value to item in Dictionary<int, string> or how to Bind to an Attribute of SelectedItem from ListBox将绑定值更新为 Dictionary<int, string> 中的项目或如何从 ListBox 绑定到 SelectedItem 的属性
【发布时间】:2014-07-11 16:53:15
【问题描述】:

对于不变的状态列表,我有一个友好名称的静态列表。这些友好的名称(绿色、黄色、红色)与底层状态值(0、1、2..)相关联。对于给定的项目,此视图将允许用户从列表中选择一个状态,并显示他们从列表中选择的状态的描述,因为它与他们当前正在编辑的项目有关。

ViewModel 公开了用户正在编辑的对象,该对象在字典中具有状态集合。例如,从 ListBox 中选择一个状态,如绿色,将使用所选项目的标记(在本例中为 0)显示字典中的文本,以显示该键的值(在本例中为“一切正常”)。

我无法手动设置 ListBoxItem 的值属性,因此我必须使用标签。该模型当前适用于在 Selected Item 更改时显示集合中每个状态的文本。 TextBox 使用 MultiBinding 到 Dictionary Collection 和 ListBox 来显示正确的文本。

这是我所拥有的:

<ListBox Name="StatusItems" Style="{StaticResource MyList}">
    <ListBoxItem Content="Green" Tag="0"></ListBoxItem>
    <ListBoxItem Content="Yellow" Tag="1" />
    <ListBoxItem Content="Yellow Audible" Tag="2" />
    <ListBoxItem Content="Yellow Flashing" Tag="3" />
    <ListBoxItem Content="Yellow Flashing Audible" Tag="4" />
    <ListBoxItem Content="Orange" Tag="5" />
    <ListBoxItem Content="Orange Audible" Tag="6" />
    <ListBoxItem Content="Orange Flashing" Tag="7" />
    <ListBoxItem Content="Orange Flashing Audible" Tag="8" />
    <ListBoxItem Content="Red" Tag="9" />
    <ListBoxItem Content="RedAudible" Tag="10" />
    <ListBoxItem Content="RedFlashing" Tag="11" />
    <ListBoxItem Content="RedFlashingAudible" Tag="12" />
    <ListBoxItem Content="Gray" Tag="13" />
</ListBox>

<TextBox MinLines="5" TextWrapping="Wrap" AcceptsReturn="True" AcceptsTab="True">
<TextBox.Text>
    <MultiBinding>
        <MultiBinding.Converter>
            <conv:DictionaryItemConverter />
        </MultiBinding.Converter>
        <Binding Path="Alarm.StatusDescriptions" Mode="TwoWay" />
        <Binding Path="SelectedItem.Tag" ElementName="StatusItems" />
    </MultiBinding>
</TextBox.Text>
</TextBox>
<Button Content="Save" Command="{Binding Path=SaveCommand}" IsEnabled="{Binding Path=Saveable}"  />

模型崩溃的地方是我尝试保存时。我不确定如何将对状态所做的更改保留回字典中的正确项目,部分原因是多值转换器。我想也许我可以在 ViewModel 上添加一个 SelectedStatus int,然后将所选列表框项的标记属性单向绑定到此属性,以便在单击保存时我会知道使用哪个键来更新描述。

我正在尝试这样的事情(当然语法是错误的)

<ListBox SelectedValue="{Binding Path=SelectedStatus, Mode=OneWayToSource, RelativeSource={RelativeSource Self.SelectedItem.Tag}}" >

我能否以某种方式将 SelectedItem 的标记属性绑定到我的视图上的属性?还是我应该放弃整个想法并走另一条路?

【问题讨论】:

  • 有更简单的方法来填充列表框。容易得多。在 MVVM 中,没有理由不使用它们。
  • @HenkHolterman 我们是说我也应该创建一些集合来绑定列表框吗?我可以这样做,只是该列表是一个静态不变的状态列表。绿色只是用户的友好名称,他们正在编辑的底层状态描述是状态 0。即使我将它绑定到不同的集合,它也不会是字典,并且它们之间不会存在一对一的关联列出所选项目和 viewmodels 字典条目。
  • 我不介意投反对票,但如果我知道出了什么问题,我会很感激,这样我以后可能会避免它。
  • 至少你应该避免使用标签。创建一个class { ColorName, Value }并在 ViewModel 或 XAML 中实例化一个列表。

标签: c# wpf mvvm binding


【解决方案1】:

使用 WPF,您理想地希望您的 UI 与您的应用程序逻辑和数据分开。

因此,您的 ListBox 中的项目列表应该存在于数据层(您的 ViewModel)的某个位置,而不是 UI 的组成部分。

如果我正确理解您的 ListBox 和 Dictionary 是如何链接的,我并不肯定,但听起来您想要这样的东西:

简化模型

public class SomeObject
{
    public int Id { get; set; }
    public string FriendlyName { get; set; }
    public string UserDefinedDescription { get; set; }
}

简化视图模型

public List<SomeObject> Data { get; set; }
public int SelectedDataId { get; set; }
public SomeObject SelectedData 
{
    get { return Data.FirstOrDefault(p => p.Id == SelectedDataId); }
}

简化视图

<ListBox ItemsSource="{Binding Data}"
    SelectedValue="{Binding SelectedDataId}"
    SelectedValuePath="Id"
    DisplayMemberPath="FriendlyName" />

<TextBlock Text="{Binding SelectedData.UserDefinedDescription}" />

【讨论】:

  • 看,我认为既然颜色的概念只存在于 View 中,那么 ViewModel 应该忽略它。颜色“绿色”对系统的其余部分毫无意义,它只知道底层状态 0。我这样做是因为不同的系统可能会以不同的方式表达状态或根本不表达状态。我想最后虽然我可能最终会接受你的建议并将 ListBox 绑定到 ViewModel 上的 Collection 以便我至少可以绑定到 Selected Value。
  • @jrandomuser :) 如果不同的系统想要使用不同的“FriendlyName”显示数据,那么他们可以将对象模型上的名称更新为他们想要的任何名称。这个想法是,无论使用何种形式的 UI 向用户显示,功能都将保持不变。
  • 如果您真的想将“FriendlyName”保留在您的对象模型之外,我建议您使用转换器将Id 值转换为您希望显示的任何字符串。代码与我在这里的示例几乎相同,除了您可能想要实现自己的ListBox.ItemTemplate,它只是一个TextBlock,它的Text 绑定到Id 属性,并使用IValueConverter 将其转换为您想要的任何适合 UI 的字符串。
  • 我只是希望 MultiBinding 值能够以两种方式返回源。我可以得到只是找到的值但不能更新它的事实让我无所适从。
  • @jrandomuser 我知道 MultiValueConverters 应该是双向的,但我不确定 MultiBindings。 this question 对你有帮助吗?
【解决方案2】:

我建议将您绑定到 ViewModel 中的列表框的颜色集合和 ViewModel 中的另一个属性用于保存所选颜色。类似下面的东西

保存颜色定义的类

public class ColorList
{
    public int ColorCode { get; set; }
    public string ColorName { get; set; }
}

以及ViewModel中的属性定义

private ColorList _selectedColor;
public List<ColorList> AvailableColors
{
    get
    {
        return this.GetColorsList();
    } 
}

public ColorList SelectedColor
{
    get
    {
        return this._selectedColor;
    }
    set
    {
        if (this._selectedColor != value)
        {
            this._selectedColor = value;
            this.RaisePropertyChanged("SelectedColor");
        }
    }
}

返回颜色列表的示例方法

public List<ColorList> GetColorsList()
{
    var result = new List<ColorList> () {
            new ColorList { ColorCode = 0, ColorName = "Green" },
            new ColorList { ColorCode = 1, ColorName = "Yellow" },
            new ColorList { ColorCode = 2, ColorName = "Yellow Audible" }
        };
    return result;
}

最后你可以将集合绑定到你的列表框,如下所示

<ListBox ItemsSource="{Binding AvailableColor}" DisplayMemberPath="ColorName"
                SelectedItem="{Binding SelectedColor, Mode=TwoWay}" />

所以现在SelectedColor 属性将具有您可以绑定到任何其他控件的选定颜色。

【讨论】:

  • 我想这可能是我最终要走的路。直到现在我一直在回避,因为我一直在考虑将颜色的想法真正视为查看特定数据。颜色本身对底层服务没有意义,不同的应用程序以不同的方式表达这些状态。最后,我可能对保持 View 和 View Model 分开的解释过于严格了。
猜你喜欢
  • 2011-07-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-24
  • 1970-01-01
  • 2019-12-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多