【问题标题】:Populate ListView upon changing combobox - proper MVVM implementation在更改组合框时填充 ListView - 正确的 MVVM 实现
【发布时间】:2018-10-09 20:21:22
【问题描述】:

我的目标是:带有 Jira 帐户名称列表的 ComboxBox 和 ListView,它将显示在 ComboBox 中选择用户时发送到 jira 的查询的响应(因为帐户名称是查询的一部分)。

我所拥有的:对 C#、WPF、MVVM 和 工作解决方案(代码如下)知之甚少,但无论如何它都不是 MVVM。所以,我已经阅读了很多关于 MVVM(relayCommand、PropertyChanged 等)的内容,但由于某种原因,我无法提出如何将该程序重构为 MVVM 的解决方案。最大的问题之一是我无法弄清楚如何向 Jira 发出该请求并导致 IQueryable 的形式适合 MVVM 模式。我的意思是,我应该把它放在哪里。

所以,请如果有人能提示我一般应该怎么做才能将此程序转换为遵循 MVVM 模式或任何其他类型的建议,我将非常感激!

MainWindow.xamls.cs

public ObservableCollection<Issue> Issues { get; set; }

private void OnNameComboChanged(object sender, EventArgs e)
{
    Issues.Clear();

    string name = ((sender as ComboBox).SelectedItem as ComboBoxItem).Content as string;
    Issues fetchedIssues = new Issues();

    var issuesList = fetchedIssues.FetchIssues(name); // returns the list of Issues in a type of --> IQueryable<Issue>
    foreach (var issue in issuesList)
    {
        Issues.Add(issue);
    }    
}

public MainWindow()
{
    Issues = new ObservableCollection<Issue>();
    InitializeComponent();
}

MainWindow.xaml

<Controls:MetroWindow x:Name="Main_Window" x:Class="Dull.MainWindow"
    ........
    DataContext="{Binding RelativeSource={RelativeSource Self}}"> <!-- how I link contexts-->
<Controls:MetroWindow.RightWindowCommands>
    <Controls:WindowCommands>
        <ComboBox x:Name="Name" SelectionChanged="OnNameComboChanged" > <!-- Combox box with nicknames -->
            <ComboBoxItem>name of the user</ComboBoxItem>
            <ComboBoxItem>another name of the user</ComboBoxItem>
        </ComboBox>
    </Controls:WindowCommands>
</Controls:MetroWindow.RightWindowCommands>
<Grid>
    <ListView x:Name="issuesListView" ItemsSource="{Binding Issues}"> <!-- ListView binded to Issues collection -->
        <ListView.ItemTemplate>
            <DataTemplate>
              <TextBlock Text="{Binding Summary}" 
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

【问题讨论】:

    标签: c# wpf mvvm jira-rest-api


    【解决方案1】:

    有各种框架,如PrismCaliburn MicroMVVMLight 等等,它们提供了编写 MVVM 设计模式应用程序的功能。上述框架提供的功能很少

    1. DelegateCommand 或 RelayCommand
    2. ViewModelLocator
    3. 容器/模块
    4. 事件聚合器

    这些功能可以轻松地以 MVVM 设计模式编写代码。但是,如果您不需要所有这些功能并且不想集成这些功能,请不要担心。

    现在,此答案中的所有对话都基于您希望在没有这些框架的情况下为您的实现编写。

    你可以参考这个博客写RelayCommand。如果您想将 View 与 ViewModel 分离,则确实需要 ICommand 实现。 ViewModel 的这些命令可以使用 Blends 的交互触发器与 View 集成(请参阅此 sample)。

    以上所有内容都是为解决您的问题所做的准备工作。按照步骤操作

    1. 创建视图模型

    以下 ViewModel 描述了您的需求:

    public class MyViewModel : INotifyPropertyChanged
    {
    
        public event PropertyChangedEventHandler PropertyChanged;
    
        private ObservableCollection<Issue> issues = new ObservableCollection<Issue>();
        public ObservableCollection<Issue> Issues { get {return issues;} }
    
        private ObservableCollection<string> users = new ObservableCollection<string>();
        public ObservableCollection<string> Users { get {return users;} }
    
        private string user;
        public string User 
        {
            get 
            {
                return user;
            }
            set
            {
                user = value;
                NotifyPropertyChanged();
            }
        }
    
        private ICommand userChangedCommand;
    
        public ICommand UserChangedCommand
        {
            get
            {
                return userChangedCommand ?? (userChangedCommand = new RelayCommand(
                    x =>
                    {
                        OnUserChanged();
                    }));
            }
        }
    
        private ICommand loadedCommand;
    
        public ICommand LoadedCommand
        {
            get
            {
                return loadedCommand?? (loadedCommand= new RelayCommand(
                    x =>
                    {
                        // Write Code here to populate Users collection.
                    }));
            }
        }
    
        private void OnUserChanged()
        {
            Issues.Clear();
    
            string name = this.User;
            Issues fetchedIssues = new Issues();
    
            var issuesList = fetchedIssues.FetchIssues(name); // returns the list of Issues in a type of --> IQueryable<Issue>
            foreach (var issue in issuesList)
            {
                Issues.Add(issue);
            }    
        }
    
        private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    
    }
    

    2。查看更改:

        <Controls:MetroWindow x:Name="Main_Window" x:Class="Dull.MainWindow" ........
        >
        <i:EventTrigger EventName="Loaded" >
            <i:InvokeCommandAction Command="{Binding LoadedCommand}" />
          </i:EventTrigger>
         </i:Interaction.Triggers>
          <Controls:MetroWindow.RightWindowCommands>
           <Controls:WindowCommands>
            <ComboBox x:Name="Name" ItemsSource="{Binding Users}" SelectionChanged="OnNameComboChanged" SelectedItem="{Binding User}" > <!-- Combox box is getting user details from ViewModel -->
                <i:Interaction.Triggers>
             <i:EventTrigger EventName="SelectionChanged" >
               <i:InvokeCommandAction Command="{Binding UserChangedCommand}" />
              </i:EventTrigger>
            </i:Interaction.Triggers>
            </ComboBox>
           </Controls:WindowCommands>
          </Controls:MetroWindow.RightWindowCommands>
        <Grid>
        <ListView x:Name="issuesListView" ItemsSource="{Binding Issues}"> <!-- ListView binded to Issues collection -->
            <ListView.ItemTemplate>
                <DataTemplate>
                  <TextBlock Text="{Binding Summary}" 
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </Grid>
    

    3.现在是最后一部分如何将 ViewModel 绑定到 View

    如果您使用的是上述框架,那么基于ViewModelLocator 功能,这将是微不足道的。但是,要在没有框架的情况下实现,您可以使用以下方法之一。 1)创建实例ViewModel并在Control的InitializeComponent方法(.Xaml.cs)中赋值

    var vm = new MyViewModel();
    this.DataContext = vm;
    

    然而这打破了纯 MVVM 设计模式

    2) 您可以在 View 本身中创建实例

       <Controls:MetroWindow x:Name="Main_Window" x:Class="Dull.MainWindow">
           <Controls:MetroWindow.DataContext>
            <VM:MyViewModel />
        </Controls:MetroWindow.DataContext>
        ...............
       </Controls:MetroWindow>
    

    【讨论】:

    • 非常感谢您的回答。我想我已经理解了布局以及它是如何工作的!如果您还有一个问题:当我在组合框中更改用户时,会添加 ListView 中的问题,因此我必须使用 Issues.Clear(),因此只会显示分配给新选定用户的问题。您能否解释一下,这应该如何使用 MVVM 或其肮脏的黑客来完成?我的意思是它可以接受还是我错过了什么?
    • 这是正确的方法。如果您看到我没有为 Issues 集合定义 setter,因为它有时会被滥用(稍后可以讨论)。所以要填充问题集合,让我们首先清除其中的所有项目并为选定的用户填充它。
    • 再次非常感谢,你真的在​​这里帮助了我 :) 对不起,如果它太多了,但我还有另一个问题:考虑到我的情况,我从另一个获得 IQueryable派对,所以它不是我的类等等,如果我不仅想在 ListView 中显示有关问题的信息(通过绑定该类的属性),而且还想根据每个问题中的某些属性在该 ListView 中进行一些条件显示,是否有可能?我的意思是,如果例如 Issue.Type = Open,那么我想在对应于 Type=Open 的那个问题的特定 ListViewItem 中显示图标?天哪,nvm,文本太多
    • 是的,有可能。您已经定义了ItemTemplate,因此用于根据您的逻辑和数据显示元素。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-08
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 2013-10-07
    相关资源
    最近更新 更多