【问题标题】:WPF - Strange behavior when disabling button in ListViewWPF - 在 ListView 中禁用按钮时的奇怪行为
【发布时间】:2020-01-15 18:11:41
【问题描述】:

我有一个ListView 有一些行。您可以在列表中选择一个项目,然后您将获得详细视图(主详细信息视图)。

在详细信息视图中,我有一个按钮。当您单击一个按钮时,它会禁用它并执行一些昂贵的操作。

问题是由于某种原因,所有行的所有按钮都被禁用? 我在这里遗漏了什么吗?

private void BtnGetStatus_Click(object sender, RoutedEventArgs e) {            
    Button btn = sender as Button;
    if (btn.DataContext is ListCollectionView view) {
        if (view.CurrentItem is MyViewModel viewModel) {
            // Strange - Disables all buttons not currently clicked
            btn.IsEnabled = false;

            BackgroundWorker _worker = new BackgroundWorker() { WorkerReportsProgress = false };
            _worker.DoWork += (se, ev) => {
                // Simulate Expensive operation
                System.Threading.Thread.Sleep(10000);                        
            };
            _worker.RunWorkerCompleted += (se, ev) => {
                // Strange - enables all buttons not just clicked
                btn.IsEnabled = true;
            };
            _worker.RunWorkerAsync();                    
        }
    }
}

这是一个演示问题的简单示例。 如果您单击一个按钮并转到其他行按钮将折叠。

只有被点击的应该是

XAML

<Window x:Class="WpfApp1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"        
    xmlns:local="clr-namespace:WpfApp1"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">

<Window.Resources>
    <DataTemplate x:Key="detailsUserTemplate">
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>

            <StackPanel>
                <Button Name="BtnGetStatus" Click="BtnGetStatus_Click"
                        Content="Click to start Expensive Operation">
                </Button>
            </StackPanel>
        </Grid>
    </DataTemplate>

    <CollectionViewSource 
          Source="{Binding Source={x:Static local:MainWindow.Users}}"   
          x:Key="dataViewSource" />
</Window.Resources>

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="3*"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <ListView Name="lv_data" 
              ItemsSource="{Binding Source={StaticResource dataViewSource}}"
              SelectionMode="Single">

        <ListView.View>
            <GridView>
                <GridViewColumn Header="ID" DisplayMemberBinding="{Binding Path=UserID}"/>
                <GridViewColumn Header="Username" DisplayMemberBinding="{Binding Path=Username}"/>
                <GridViewColumn Header="Is Enabled" DisplayMemberBinding="{Binding Path=IsEnabled}"/>
            </GridView>
        </ListView.View>
    </ListView>

    <ContentControl Grid.Column="1"
                    Width="Auto"
                    ContentTemplate="{StaticResource detailsUserTemplate}"
                    Content="{Binding Source={StaticResource dataViewSource}}">
    </ContentControl>
</Grid>

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;

namespace WpfApp1
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        public static ObservableCollection<User> Users
        {
            get
            {
                return new ObservableCollection<User>
                {
                    new User {UserID = 1, Username = "user 1", IsEnabled = true},
                    new User {UserID = 2, Username = "User 2", IsEnabled =true},
                    new User {UserID = 3, Username = "Username 3", IsEnabled = true},
                    new User {UserID = 4, Username = "Username 4", IsEnabled = false}
                };
            }
        }

        private void BtnGetStatus_Click(object sender, RoutedEventArgs e)
        {
            Button btn = sender as Button;

            btn.IsEnabled = false;
            btn.Visibility = Visibility.Collapsed;

            BackgroundWorker worker = new BackgroundWorker()
            {
                WorkerReportsProgress = false
            };

            worker.DoWork += (se, ev) =>
            {
                // Simulate expensive
                System.Threading.Thread.Sleep(10000);
            };

            worker.RunWorkerCompleted += (se, ev) =>
            {
                btn.IsEnabled = true;
                btn.Visibility = Visibility.Visible;
            };

            worker.RunWorkerAsync();                    
        }
    }

    public class User
    {
        public int UserID { get; set; }
        public string Username { get; set; }
        public bool IsEnabled { get; set; }
    }
}

【问题讨论】:

  • 似乎您的第二个事件处理程序没有被考虑在内......我会寻求它
  • 你应该看看 MVVM。使该命令从您用于每一行的视图模型中公开。然后,您可以使用 canexecute 来禁用命令并知道单击了哪个按钮。还要查看异步等待并在后台线程中运行任务,等待结果然后切换回启用的标志。
  • @Andy 我没有提到我正在使用 .net 4.0。 MVVM 在这里没有任何区别。问题是一旦调用 btn.IsEnabled,每个按钮都会被禁用。一旦完成昂贵的操作并且 btn.IsEnabled = true 所有按钮都会再次启用。这是为什么 ?谢谢
  • 您没有提供您的问题的最小复制,所以任何人都可以做的就是猜测。 Click 是一个冒泡的路由事件。我猜你正在某个地方处理它,所有事件都会冒泡。您可能可以在列表视图级别处理它并使用 e.originalsource 作为按钮。 Mvvm 会有所作为,因为触发的 icommand 将位于用户单击按钮的特定行的数据上下文中。
  • @Andy 我添加了一个工作示例来演示该问题。

标签: c# wpf listview button


【解决方案1】:

问题是您只有一个按钮,这与列表视图的选定项无关。

你禁用它,那一个按钮就被禁用了。

您需要将该状态与特定用户相关联,并将模板化按钮的状态绑定到该状态。

    <Window.Resources>
        <DataTemplate DataType="{x:Type local:User}">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"/>
                </Grid.RowDefinitions>

                <StackPanel>
                    <Button Name="BtnGetStatus"
                            Click="BtnGetStatus_Click"
                            Visibility="{Binding Visibility}"
                            IsEnabled="{Binding IsEnabled}"
                        Content="Click to start Expensive Operation">
                    </Button>
                </StackPanel>
            </Grid>
        </DataTemplate>

        <CollectionViewSource 
          Source="{Binding Source={x:Static local:MainWindow.Users}}"   
          x:Key="dataViewSource" />
    </Window.Resources>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="3*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <ListView Name="lv_data" 
              ItemsSource="{Binding Source={StaticResource dataViewSource}}"
              SelectionMode="Single">

            <ListView.View>
                <GridView>
                    <GridViewColumn Header="ID" DisplayMemberBinding="{Binding Path=UserID}"/>
                    <GridViewColumn Header="Username" DisplayMemberBinding="{Binding Path=Username}"/>
                    <GridViewColumn Header="Is Enabled" DisplayMemberBinding="{Binding Path=IsEnabled}"/>
                </GridView>
            </ListView.View>
        </ListView>

        <ContentControl Grid.Column="1"
                    Width="Auto"
                    Content="{Binding ElementName=lv_data, Path=SelectedItem}">
        </ContentControl>
    </Grid>
</Window>

按钮绑定它的状态。

每个用户现在都有一个状态可以呈现给它的模板按钮,并且与所选项目相关。

我已经实现了 inotifypropertychanged,因此当属性更改时,视图将获取新值。

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        public static ObservableCollection<User> Users
        {
            get
            {
                return new ObservableCollection<User>
                {
                    new User {UserID = 1, Username = "user 1", IsEnabled = true},
                    new User {UserID = 2, Username = "User 2", IsEnabled =true},
                    new User {UserID = 3, Username = "Username 3", IsEnabled = true},
                    new User {UserID = 4, Username = "Username 4", IsEnabled = false}
                };
            }
        }

        private async void BtnGetStatus_Click(object sender, RoutedEventArgs e)
        {
            Button btn = sender as Button;
            User selectedUser = btn.DataContext as User;

            if (selectedUser==null)
            {
                return;
            }
            selectedUser.IsEnabled = false;
            selectedUser.Visibility = Visibility.Collapsed;

            await Task.Run(() => doStuffAsync(selectedUser));
            selectedUser.IsEnabled = true;
            selectedUser.Visibility = Visibility.Visible;
        }
        private async Task<bool> doStuffAsync(User user)
        {
            // Do something expensive
            await Task.Delay(10000);
            return true;
        }

    }

    public class User : INotifyPropertyChanged
    {
        public int UserID { get; set; }
        public string Username { get; set; }

        private bool isEnabled;

        public bool IsEnabled
        {
            get { return isEnabled; }
            set { isEnabled = value; RaisePropertyChanged(); }
        }
        private Visibility visibility;

        public Visibility Visibility
        {
            get { return visibility; }
            set { visibility = value; RaisePropertyChanged(); }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged([CallerMemberName] String propertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }

    }

【讨论】:

  • 谢谢这是解决方案。我真的不知道这是默认行为。 WPF 新手 :-) 您能否推荐一些链接,我可以在其中找到有关此行为的更多信息?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-11-11
  • 2021-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多