Кнопка не нажимается при рендеринге окна

#.net #wpf #button #rendering

#.net #wpf #кнопка #рендеринг

Вопрос:

У меня есть приложение WPF, которое не позволит мне нажимать кнопку, пока не пройдет немного времени, но это поведение является случайным, когда оно происходит только некоторое время. Большую часть времени окно отображается, и я могу сразу нажать указанную кнопку.

Главное окно:

     <views:StatusBox DockPanel.Dock="Left" Visibility="{Binding Editing, Converter={StaticResource BoolToVisibleConverter}}" />
</DockPanel>
  

Управление пользователем:

 <StackPanel Orientation="Horizontal">
    <!--Read Button-->
    <Button Content="Read "   x:Name="ReadBtn"     Command="{Binding ReadCMD}"    Padding="10,5" Margin= "10,0" IsEnabled="{Binding ReadEnabled}" />

    <!--Save Button-->
    <Button Content="Save "   x:Name="SaveBtn"     Command="{Binding SaveCMD}"    Padding="10,5" Margin= "10,0" IsEnabled="{Binding SaveEnabled}" 
            Background="{StaticResource {x:Static SystemColors.ControlLightBrushKey}}">
        <Button.Style>
            <Style TargetType="Button">
                <Style.Triggers>
                    <DataTrigger Binding="{Binding IsSaveNeeded}" Value="Yes">
                        <DataTrigger.EnterActions>
                            <BeginStoryboard x:Name="FlashBackground">
                                <Storyboard BeginTime="00:00:00" RepeatBehavior="Forever" >
                                    <ColorAnimation  Storyboard.TargetProperty="Background.Color" Duration="00:00:00.25" AutoReverse="True" To="Red" />
                                </Storyboard>
                            </BeginStoryboard>
                        </DataTrigger.EnterActions>

                        <DataTrigger.ExitActions>
                            <StopStoryboard BeginStoryboardName="FlashBackground" />
                        </DataTrigger.ExitActions>
                    </DataTrigger>
                </Style.Triggers>
            </Style>
        </Button.Style>
    </Button>

    <!--Load Button-->
    <Button Content="Load "   x:Name="LoadBtn"     Command="{Binding LoadCMD}"    Padding="10,5" Margin= "10,0" IsEnabled="{Binding LoadEnabled}" />

    <!--Print Button-->
    <Button Content="Print"   x:Name="PrintBtn"    Command="{Binding PrintCMD}"   Padding="10,5" Margin= "10,0" IsEnabled="{Binding PrintEnabled}" />

    <!--Exit Button-->
    <Button Content="Exit"    x:Name="ExitBtn"     Command="{Binding ExitCMD}"    Padding="10,5" Margin= "10,0" />
</StackPanel>
  

Просмотр модели

 private bool _tab1_Selected;
    public bool tab1_Selected
    {
        get
        {
            return _tab1_Selected;
        }
        set
        {
            _tab1_Selected = value;

            if (value)
            {
                CommMode = (byte)PacketType.tab1_DUMP;

                if (tab1needsSaving)
                {
                    IsSaveNeeded = SaveNeeded.Yes;
                }
                else
                {
                    IsSaveNeeded = SaveNeeded.No;
                }
            }

            NotifyPropertyChanged("tab1_Selected");
            NotifyPropertyChanged("SaveEnabled");
            NotifyPropertyChanged("ProgramEnabled");
            NotifyPropertyChanged("Editing");
        }
    }


    private bool _tab2_Selected;
    public bool tab2_Selected
    {
        get
        {
            return _tab2_Selected;
        }
        set
        {
            _tab2_Selected = value;

            if (value)
            {
                CommMode = (byte)PacketType.tab2_DUMP;

                if (tab2needsSaving)
                {
                    IsSaveNeeded = SaveNeeded.Yes;
                }
                else
                {
                    IsSaveNeeded = SaveNeeded.No;
                }
            }

            NotifyPropertyChanged("tab2_Selected");
            NotifyPropertyChanged("SaveEnabled");
            NotifyPropertyChanged("ProgramEnabled");
            NotifyPropertyChanged("Editing");
        }
    }


public bool Editing
    {
        get
        {
            return tab2_Selected || tab1_Selected;
        }
    }

 private bool _saveEnabled;
    public bool SaveEnabled
    {
        get
        {
            //return _saveEnabled;
            return ((tab1_Selected amp;amp; tab1Data != null amp;amp; tab1Data.Count > 0) || (tab2_Selected amp;amp; tab2Data != null amp;amp; tab2Data.Count > 0) amp;amp; _saveEnabled);
        }
        set
        {
            _saveEnabled = value;
            NotifyPropertyChanged("SaveEnabled");
        }
    }

 private bool _programEnabled;
    public bool ProgramEnabled
    {
        get
        {
            //return _saveEnabled;
            return ((tab1_Selected amp;amp; tab1Data != null amp;amp; tab1Data.Count > 0) || (tab2_Selected amp;amp; tab2Data != null amp;amp; tab2Data.Count > 0) amp;amp; _programEnabled);
        }
        set
        {
            _programEnabled = value;
            NotifyPropertyChanged("ProgramEnabled");
        }
    }
  

Команды:

     public ICommand ReadCMD     { get; set; }
    public ICommand SaveCMD     { get; set; }
    public ICommand LoadCMD     { get; set; }
    public ICommand ProgramCMD  { get; set; }
    public ICommand PrintCMD    { get; set; }
    public ICommand ExitCMD     { get; set; }

    // In MainVM constructor
    ReadCMD = new RelayCommand(ReadSettings);
    SaveCMD = new RelayCommand(SaveSettings);
    LoadCMD = new RelayCommand(LoadSettings);
    ProgramCMD = new RelayCommand(ProgramSettings);
    PrintCMD = new RelayCommand(PrintSettings);
    ExitCMD = new RelayCommand(ExitProgram);
  

Класс команды ретрансляции:

  public class RelayCommand : ICommand
{
    private Action<object> execute;

    private Predicate<object> canExecute;

    private event EventHandler CanExecuteChangedInternal;

    public RelayCommand(Action<object> execute)
        : this(execute, DefaultCanExecute)
    {
    }

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }

        if (canExecute == null)
        {
            throw new ArgumentNullException("canExecute");
        }

        this.execute = execute;
        this.canExecute = canExecute;
    }

    public event EventHandler CanExecuteChanged
    {
        add
        {
            CommandManager.RequerySuggested  = value;
            this.CanExecuteChangedInternal  = value;
        }

        remove
        {
            CommandManager.RequerySuggested -= value;
            this.CanExecuteChangedInternal -= value;
        }
    }

    public bool CanExecute(object parameter)
    {
        return this.canExecute != null amp;amp; this.canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        this.execute(parameter);
    }

    public void OnCanExecuteChanged()
    {
        EventHandler handler = this.CanExecuteChangedInternal;
        if (handler != null)
        {
            //DispatcherHelper.BeginInvokeOnUIThread(() => handler.Invoke(this, EventArgs.Empty));
            handler.Invoke(this, EventArgs.Empty);
        }
    }

    public void Destroy()
    {
        this.canExecute = _ => false;
        this.execute = _ => { return; };
    }

    private static bool DefaultCanExecute(object parameter)
    {
        return true;
    }
}
  

Обновить:

Я переключился с a DockPanel на a StackPanel и установил ширину на StatusBox вместо того, чтобы разрешить LastChildFill настраивать поле состояния, и это устранило мою проблему. Несмотря на то, что я поместил рамку вокруг StatusBox , чтобы увидеть, где она отображается, она все еще вызывала у меня проблемы.

Комментарии:

1. Пожалуйста, объясните свойства SaveEnabled etc или опубликуйте больше кода. Трудно найти ошибку в привязке, если половина ее отсутствует.

2. @Ihildebrandt — я добавил для вас свойства SaveEnabled и ProgramEnabled.

3. Это кнопка с SaveEnabled , которая вызывает проблемы? И кстати, как выглядит ваша ICommand реализация?

4. Я добавил информацию о ICommand. Это кнопка чтения, которая вызывает у меня проблему. Кнопка сохранения отключена при первом запуске программы.

Ответ №1:

Вам не нужно явно привязываться к свойству SaveEnabled, чтобы кнопка была отключена / включена в зависимости от того, может ли команда быть выполнена. Просто включите свою логику CanExecute в предикат вашей RelayCommand, и если свойство изменится, вызовите OnCanExecuteChanged в вашей команде, чтобы включить кнопку.

Редактировать

Что-то вроде этого

 private ICommand saveCMD;

public ICommand SaveCMD     
{ 
   get
   {
      if(this.saveCMD == null)
      {
         this.saveCMD = new RelayCommand(this.SaveSettings, this.CanSaveSettings)
      }

      return saveCMD;
   }
}

public bool CanSaveSettings()
{
     return ((tab1_Selected amp;amp; tab1Data != null amp;amp; tab1Data.Count > 0) || (tab2_Selected amp;amp; tab2Data != null amp;amp; tab2Data.Count > 0) amp;amp; _saveEnabled);
}
  

И тогда, по вашему мнению, вы должны иметь возможность просто использовать это, и это должно сработать:

 <Button Content="Save" x:Name="SaveBtn" Command="{Binding SaveCMD}" Padding="10,5" Margin= "10,0"/> 
  

Комментарии:

1. @Ihildebrandt, не могли бы вы привести пример. Я делал это раньше в других проектах и не мог заставить пользовательский интерфейс обновлять кнопки, и мне пришлось переключиться на свойство, которое вызывало событие изменения свойства. Следовательно, почему я написал это так, как оно есть.

2. Отредактировал мой основной пост. Пожалуйста, проверьте, устраняет ли это вашу проблему.

3. Я попробовал это, и это не помогло. Время от времени после запуска программы я все еще не могу сразу нажать кнопку чтения.

4. Может быть, логика для команды чтения занимает слишком много времени? Попробуйте установить точку останова и выяснить, когда предикат для команды вызывается в первый раз, а не в первый раз true.