简体   繁体   English

命名动态生成的 combobox 项目

[英]Name dynamically generated combobox items

I am currently working on a WPF project in .NET Core 3.1.我目前正在 .NET Core 3.1 中的 WPF 项目上工作。 During the execution I have to read some file names to a combobox.在执行期间,我必须将一些文件名读取到 combobox。 I do that by first adding them to a list.我首先将它们添加到列表中。 After that I iterate through that list and add them to the combobox.之后,我遍历该列表并将它们添加到 combobox。

Problem Because they should only be accessible if an Item from another combobox is selected, I need to disable the items I just added.问题因为只有在选择了来自另一个 combobox 的项目时它们才应该可以访问,所以我需要禁用我刚刚添加的项目。 Because of that, and to later enable them, I need to name them.因此,为了以后启用它们,我需要命名它们。 But I could not figure our how to do that.但我不知道我们该怎么做。

Here is my code:这是我的代码:

        string[] allAircraft;
        allAircraft = Directory.GetFiles(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + @"\ApplicationName\Aircraft\", "*.lxml", SearchOption.TopDirectoryOnly);
        foreach (var currentAircraft in allAircraft)
        {
            aircraft.Add(System.IO.Path.GetFileNameWithoutExtension(currentAircraft));
        }

        foreach (string currentListItem in aircraft)
        {
            comboAircraft.Items.Add(currentListItem);
        }

        foreach (ComboBoxItem currentComboItems in comboAircraft.Items)
        {
            currentComboItems.Name = "comboAircraftItems" + currentComboItems.Content;
        }

At the line foreach (ComboBoxItem currentItems in comboAircraft.Items) I get the Exception:foreach (ComboBoxItem currentItems in comboAircraft.Items)行我得到异常:

System.InvalidCastException: 'Unable to cast object of type 'System.String' to type 'System.Windows.Controls.ComboBoxItem'.'

I am really running out of ideas.我真的没有想法了。 I googled one entire day but could not find anything.我用谷歌搜索了一整天,但找不到任何东西。

Have a nice day everyone!祝大家拥有美好的一天!

@ t-kldw, Your task is very difficult to solve when working directly with UI elements. @ t-kldw,直接使用 UI 元素时,您的任务很难解决。 But it is very simple to solve, if you separate the Data and their Presentation, as it should be in MVVM.但是解决起来很简单,如果你把数据和他们的表现分开,就像在 MVVM 中一样。

Unfortunately, what you wrote contains few details for an example with full implementation.不幸的是,您所写的内容几乎没有包含完整实现示例的详细信息。 I can give only partial advice.我只能给出部分建议。 It is necessary to create an additional container-class for the data needed in one combo box.有必要为一个组合框中所需的数据创建一个额外的容器类。 This class MUST have an INotifyPropertyChanged implementation.这个 class 必须有一个 INotifyPropertyChanged 实现。

To simplify, take implementation from the topic OnPropertyChanged and instance property为了简化,从主题OnPropertyChanged 和实例属性中获取实现

/// <summary>Base class implementing INotifyPropertyChanged.</summary>
public abstract class BaseINPC : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>Called AFTER the property value changes.</summary>
    /// <param name="propertyName">The name of the property.
    /// In the property setter, the parameter is not specified. </param>
    public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

An example class with two properties: File Name and IsEnabled to allow selection.示例 class 具有两个属性:文件名和 IsEnabled 以允许选择。

public class FileItem : BaseINPC
{
    private string _fileName;
    private bool _isEnabled;

    public string FileName { get => _fileName; set { _fileName = value; RaisePropertyChanged(); } }
    public bool IsEnabled { get => _isEnabled; set { _isEnabled = value; RaisePropertyChanged(); } }
}

So that I can give further advice, you need to provide more details of your task.为了让我可以提供进一步的建议,您需要提供有关您的任务的更多详细信息。 For example, I don't even have a clue why currentComboItems.Name might be needed.... I think that you are clearly doing something wrong.例如,我什至不知道为什么可能需要 currentComboItems.Name ......我认为你显然做错了什么。

You are trying to convert String to ComboBoxItem .您正在尝试将String转换为ComboBoxItem When you populate your combobox with data, you add String , not ComboBoxItem .当您使用数据填充 combobox 时,您添加的是String ,而不是ComboBoxItem So you should use String in the last foreach loop.所以你应该在最后一个foreach循环中使用String

 foreach (string currentComboItems in comboAircraft.Items) { currentComboItems.Name = "comboAircraftItems" + currentComboItems; }

Another solution is to add ComboBoxItem to your combobox instead of String .另一种解决方案是将ComboBoxItem添加到您的 combobox 而不是String

foreach (string currentListItem in aircraft)
{
    ComboBoxItem item = new ComboBoxItem()
    {
        Content = currentListItem,
        Name = "comboAircraftItems" + currentListItem
    };
    comboAircraft.Items.Add(item);
}

ItemsControl.Items only holds data objects. ItemsControl.Items仅保存数据对象。 In order to render this items, each is wrapped into a container eg, ComboBoxItem .为了呈现这些项目,每个项目都被包装到一个容器中,例如ComboBoxItem These UI containers are generated by a ItemContainerGenerator , which is associated with the current ItemsControl .这些 UI 容器由与当前ItemsControl关联的ItemContainerGenerator生成。 The ItemContainerGenerator is also responsible to track the mapping between the data item and its UI container. ItemContainerGenerator还负责跟踪数据项与其 UI 容器之间的映射。
To obtain the container of an item you use the ItemsControl.ItemContainerGenerator :要获取项目的容器,请使用ItemsControl.ItemContainerGenerator

DependencyObject itemContainer = ItemContainerGenerator.ContainerFromItem(item);

Your simplified code becomes:您的简化代码变为:

List<string> allAircrafts = Directory.EnumerateFiles(
    Path.Combine(
      Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
      @"\ApplicationName\Aircraft\"),
    "*.lxml",
    SearchOption.TopDirectoryOnly)
  .Select(Path.GetFileNameWithoutExtension)
  .ToList();

comboAircraft.ItemsSource = allAircrafts;

foreach (string item in allAircrafts)
{
  var itemContainer = comboAircraft.ItemContainerGenerator.ContainerFromItem(item) as ComboBoxItem;
  itemContainer.Name = "comboAircraftItems" + itemContainer.Content;
}

But note, that the approach you chose has issues.但请注意,您选择的方法存在问题。 When UI virtualization is enabled for ItemsCopntrol , then only the visible items have their containers generated.当为ItemsCopntrol启用 UI 虚拟化时,只有可见项目才会生成其容器。 Items out side the viewport of the scroll viewer are not generated and can't be returned by the ItemContainerGenerator .滚动查看器视口之外的项目不会生成,也不能由ItemContainerGenerator返回。
Since UI virtualization is not enabled for the ComboBox by default, this still can work, as long you don't enable this performance optimization feature.由于ComboBox默认没有启用 UI 虚拟化,所以只要不启用此性能优化功能,这仍然可以工作。

The optimal solution would need you to create a filtered source collection for the ComboBox that displays the filtered items.最佳解决方案需要您为显示过滤项目的ComboBox创建过滤源集合。 This way you won't clutter the control with items that are not of interest for the user and are not selectable anyway.这样,您就不会因为用户不感兴趣且无论如何都无法选择的项目而使控件混乱。

Create a collection of type ObservableCollection<string> which contains the selected items of the primary ComboBox you named comboAircraft .创建一个ObservableCollection<string>类型的集合,其中包含您命名为comboAircraft的主ComboBox的选定项目。
Set this collection as ItemsSource of the second filtered ComboBox .将此集合设置为第二个过滤ComboBoxItemsSource

Alternatively create a CollectionViewSource of the original source collection and bind it to the secondary ComboBox .或者,创建原始源集合的CollectionViewSource并将其绑定到辅助ComboBox ICollectionView supports filtering. ICollectionView支持过滤。 The proper solution正确的解决方案

Using either of the two recommended solutions eliminates the requirement to care about item containers.使用两种推荐的解决方案中的任何一种,都无需关心物品容器。 This adds flexibility and simplifies your code.这增加了灵活性并简化了您的代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM