wpf - 禁用除单击控件之外的整个窗口

标签 wpf

我有一个包含多个按钮的 Window。我需要跟踪按下按钮的时间 (MouseDown) - 开始操作 - 以及何时释放或离开按钮(MouseUpMouseLeave ) 结束/取消操作。

取消可能需要一段时间,在此期间我需要防止用户点击另一个按钮。这是忙碌指示的经典案例。当操作结束时,我可以显示一个带有覆盖层的全局忙碌指示器。然而,在可用性方面,有更好的解决方案,但我正在努力寻找实现它的方法。

所以这就是我想要实现的目标:

1) 初始窗口状态: initial window state

2) 一旦按下按钮,窗口的其余部分应该变灰(或模糊效果)并被“禁用”。 窗口的其余部分还包括其他几个输入控件(TabControl、带有Button 的通知 View 、ToggleButton 等. -- 所有的都需要被禁用。所以它真的是“所有的 child ,除了那个被点击的 Button”)) Button pressed - rest of Window disabled

3) 当按钮被释放时,操作被取消,但是因为这可能需要一段时间,所以按钮上应该显示忙碌指示(我知道怎么做这个 部分) enter image description here

4) 一旦操作结束,窗口就会恢复到初始状态: enter image description here

有两个重要条件:

  • 在同一 Window 上还有其他相同类型(相同功能和行为)的按钮。因此,仅仅将 Panel.ZIndex 设置为一个常量 是行不通的。
  • 同一窗口中还有其他几个输入控件。那些也需要被“禁用”。
  • 显示覆盖层/使 Window 的其余部分变灰可能不会触发鼠标事件,如 MouseUpMouseLeave(否则操作会立即被取消)。

研究完成

禁用窗口:我研究过使用 IsEnabled 属性。但是,默认情况下它会传播到所有子元素,您不能覆盖一个特定子元素的值。 我实际上已经找到了一种改变这种行为的方法(在 SO 上),但我担心改变这种行为可能会弄乱其他地方的东西(而且,这真的出乎意料 - future 的开发人员会把它视为魔法)。另外,我不喜欢控件在禁用状态下的样子,弄乱它会很麻烦。

所以我更喜欢使用某种“覆盖”,但它会将后面的东西变灰(我猜 color=grey, opacity=0.5 结合 IsHitTestVisible=True 将是一个开始?)。但这里的问题是我不知道如何在覆盖层顶部获得 一个 按钮,而窗口的所有其余部分都留在后面...

编辑:使用 ZIndex 似乎只适用于同一级别的项目(至少对于网格)。所以这也不是一个选择:(

最佳答案

非常有趣的问题。我试着像下面这样解决它:

  1. 使用唯一标识符在您的按钮上定义 Tag 属性(我使用了数字,但如果您将 Tag 设置为您的按钮所做的事情,这将有意义)并定义 Style 如下所示:

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525"
            xmlns:converter="clr-namespace:WpfApplication1" Tag="Win" >
    <Window.Resources>
        <converter:StateConverter x:Key="StateConverter"/>
        <Style TargetType="Button">
            <Style.Triggers>
                <DataTrigger Value="False">
                    <DataTrigger.Binding>
                        <MultiBinding Converter="{StaticResource StateConverter}">
                            <Binding Path="ProcessStarter"/>
                            <Binding Path="Tag" RelativeSource="{RelativeSource Self}"/>
                        </MultiBinding>
                    </DataTrigger.Binding>
                    <DataTrigger.Setters>
                        <Setter Property="Background" Value="White"/>
                        <Setter Property="Opacity" Value="0.5"/>
                        <Setter Property="IsEnabled" Value="False"/>
                    </DataTrigger.Setters>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Resources>
    <Window.Style>
        <Style TargetType="Window">
            <Style.Triggers>
                <DataTrigger Value="False">
                    <DataTrigger.Binding>
                        <MultiBinding Converter="{StaticResource StateConverter}">
                            <Binding Path="ProcessStarter"/>
                            <Binding Path="Tag" RelativeSource="{RelativeSource Self}"/>
                        </MultiBinding>
                    </DataTrigger.Binding>
                    <DataTrigger.Setters>
                        <Setter Property="Background" Value="LightGray"/>
                    </DataTrigger.Setters>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </Window.Style>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <Button Tag="1" Content="1" Command="{Binding ActionCommand}" CommandParameter="{Binding Tag, RelativeSource={RelativeSource Self}}"/>
        <Button Tag="2" Grid.Column="1" Content="2" Command="{Binding ActionCommand}" CommandParameter="{Binding Tag, RelativeSource={RelativeSource Self}}"/>
        <Button Tag="3" Grid.Column="2" Content="3" Command="{Binding ActionCommand}" CommandParameter="{Binding Tag, RelativeSource={RelativeSource Self}}"/>
    </Grid>
    

  2. 然后像下面这样在您的 VM 中捕获调用 Command 的按钮的标签(这里我已经在后面的代码中定义了所有属性)

    public partial class MainWindow : Window, INotifyPropertyChanged
    {
    private const string _interactiveTags = "1:2:3:Win";
    private BackgroundWorker _worker;
    
    public MainWindow()
    {
        InitializeComponent();
        _worker = new BackgroundWorker();
        _worker.DoWork += _worker_DoWork;
        _worker.RunWorkerCompleted += _worker_RunWorkerCompleted;
    
        ActionCommand = new DelegateCommand(CommandHandler);
        DataContext = this;
    
    }
    
    private void CommandHandler(object obj)
    {
        ProcessStarter = obj.ToString();
        if (!_worker.IsBusy)
        {
            _worker.RunWorkerAsync();
        }
    }
    
    public ICommand ActionCommand { get; private set; }
    
    void _worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        ProcessStarter = _interactiveTags;
    }
    
    void _worker_DoWork(object sender, DoWorkEventArgs e)
    {
        Thread.Sleep(300);
    }
    
    public string _processStarter = _interactiveTags;
    public string ProcessStarter
    {
        get { return _processStarter; }
        set
        {
            _processStarter = value;
            RaisePropertyChanged("ProcessStarter");
        }
    }
    
  3. 最后是转换器,如果这是引发命令或正在做某事的按钮,则返回

    public class StateConverter : IMultiValueConverter
     {
        public string Name { get; set; }
    
        public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
       {
            var tags = (values[0] as string).Split(':');
           return tags.Contains(values[1] as string);
    
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
       {
          throw new NotImplementedException();
       }
     }
    

我使用 Backgroundworker 模拟了繁重的工作,并让线程休眠了 300 毫秒。测试了它。工作正常。

关于wpf - 禁用除单击控件之外的整个窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29839891/

相关文章:

.net - WPF控制性能

c# - DependencyProperty 绑定(bind)问题

wpf - 如何读取 WPF UserControl 中传递的参数?

WPF:System.Windows.Interop.InteropBitmap 到 System.Drawing.Bitmap

WPF 数据网格所有列的文本换行

c# - Ms Sql server 2008 r2 数据同步

c# - 自定义控件中的 WPF 数据绑定(bind)

wpf - 如何将 View 中控件的 UI 调度程序传递给 ViewModel

c# - 为什么关闭在单元测试中创建的窗口会引发 InvalidComObjectException?

C# TreeView 并绑定(bind)到数据库