RelayCommand CanExecute zeigt keine Wirkung

  • WPF

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von MichaHo.

    RelayCommand CanExecute zeigt keine Wirkung

    Hi,
    in meinem Projekt möchte ich einen Button Enablen/Disabeln, dazu habe ich ein Property im ViewModel CanCloseAllTabPages welches nur zurück gibt, ob die ObservableCollection.Count > 0 ist.

    Vorab, ich nutze die RelayCommand Klasse von @Nofear23m samt PropertyObserver und PropertyObserverNode (ViewModel Projekt ist .NetStandard).

    Der Command:

    C#-Quellcode

    1. public ICommand CloseAllTabsCommand { get; set; }

    Die beiden Properties um die es geht:

    C#-Quellcode

    1. public ObservableCollection<TabPageViewModel> TabPages { get; set; }
    2. public bool CanCloseAllTabPages => TabPages.Count > 0;


    die Initialisierung des Command:

    C#-Quellcode

    1. CloseAllTabsCommand = new RelayCommand(c => ClearTabPages()).ObservesCanExecute(() => CanCloseAllTabPages);


    Das Binding im UserControl:

    XML-Quellcode

    1. <Button Content="{StaticResource CloseTabsIcon}"
    2. Style="{StaticResource HeaderIconButton}" Height="40"
    3. Command="{Binding CloseAllTabsCommand}" ToolTip="Alle Tabs schließen" />

    Dazu noch der Style, hier ist ein Trigger für "IsEnabled = False"

    XML-Quellcode

    1. <Style x:Key="HeaderIconButton" TargetType="{x:Type Button}" BasedOn="{StaticResource BaseStyle}">
    2. <Setter Property="Background" Value="Transparent"/>
    3. <Setter Property="Foreground" Value="{StaticResource ForegroundDefaultBrush}"/>
    4. <Setter Property="BorderThickness" Value="0"/>
    5. <Setter Property="FontSize" Value="{StaticResource FontSizeXXLarge}"/>
    6. <Setter Property="FontFamily" Value="{StaticResource FontAwesome2}"/>
    7. <Setter Property="Padding" Value="25"/>
    8. <Setter Property="Margin" Value="0 "/>
    9. <Setter Property="Width" Value="{Binding ActualHeight, RelativeSource={RelativeSource Self}}" />
    10. <Setter Property="Template">
    11. <Setter.Value>
    12. <ControlTemplate TargetType="{x:Type ButtonBase}">
    13. <Border x:Name="border"
    14. BorderBrush="{TemplateBinding BorderBrush}"
    15. BorderThickness="{TemplateBinding BorderThickness}"
    16. Background="{TemplateBinding Background}"
    17. SnapsToDevicePixels="True">
    18. <Grid>
    19. <Viewbox>
    20. <TextBlock x:Name="text" Text="{TemplateBinding Content}"
    21. Focusable="False"
    22. FontFamily="{TemplateBinding FontFamily}"
    23. HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
    24. Margin="{TemplateBinding Padding}"
    25. SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
    26. VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
    27. </Viewbox>
    28. </Grid>
    29. </Border>
    30. <ControlTemplate.Triggers>
    31. <EventTrigger RoutedEvent="MouseEnter">
    32. <BeginStoryboard>
    33. <Storyboard>
    34. <ColorAnimation To="{StaticResource CharLightBlue}" Duration="0:0:0.3" Storyboard.TargetName="text" Storyboard.TargetProperty="Foreground.Color"/>
    35. </Storyboard>
    36. </BeginStoryboard>
    37. </EventTrigger>
    38. <EventTrigger RoutedEvent="MouseLeave">
    39. <BeginStoryboard>
    40. <Storyboard>
    41. <ColorAnimation To="{StaticResource DefaultMain}" Duration="0:0:0.3" Storyboard.TargetName="text" Storyboard.TargetProperty="Foreground.Color"/>
    42. </Storyboard>
    43. </BeginStoryboard>
    44. </EventTrigger>
    45. <Trigger Property="IsEnabled" Value="False">
    46. <Setter Property="Background" TargetName="border" Value="Transparent"/>
    47. <Setter Property="Foreground" TargetName="text" Value="{StaticResource ForegroundLightBrush}"/>
    48. </Trigger>
    49. </ControlTemplate.Triggers>
    50. </ControlTemplate>
    51. </Setter.Value>
    52. </Setter>
    53. </Style>

    Wenn ich nun das Programm starte, ist der Button Disabled das ist correct, da TabPages = 0 ist.
    Wenn ich dann eine TabPage hinzufüge ist der Button leider immernoch disabled, TabPages.Count ist dann aber 1 und CanCloseAllTabPages ist true.

    Hab ich da beim Binding irgendwas vergessen?

    P.S. vielleciht noch als RandInfo, ich nutze das NugetPaket FoodyWeaver, welches alle Properties, die INotifyPropertyChanged implementieren überwacht und beim Kompilieren dann OnPropertyChanged in den Setter hinzufügt.
    "Hier könnte Ihre Werbung stehen..."
    Hallo

    Ich denke du hast dich wohl hier vertan:

    C#-Quellcode

    1. public bool CanCloseAllTabPages => TabPages.Count > 0;

    mach mal

    C#-Quellcode

    1. public bool CanCloseAllTabPages = TabPages.Count > 0;


    Ansonsten müsste ich das mal selbst probieren (ohne FoodyWeaver).

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hi Sascha,

    C#-Quellcode

    1. public bool CanCloseAllTabPages => TabPages.Count > 0;
    ist die C# Kurzschreibweise für

    C#-Quellcode

    1. public bool CanCloseAllTabPages {get { return TabPages.Count > 0}};


    Es scheint aber daran zu liegen, keine Ahnung ob das nun an Foody Weaver liegt.
    Mach ich das so

    C#-Quellcode

    1. public bool CanCloseAllTabPages {get;set;}
    und setze dann True/False selbst, dann klappt es.

    Allerdings, und das liegt wphl irgendwo im Style, ist der Button zwar disabled, aber die Farbe ist die gleiche, als wäre enabled. Misteriös...
    "Hier könnte Ihre Werbung stehen..."
    Ne, dann weis ich schon woran es liegt. Und zwar daran das du der WPF nicht sagst das es CanCloseAllTabPages neu auswerten soll.

    Sprich. Du musst wenn sich etwas an der ObservableCollection tut dieses neu Auswerten lassen. Also musst du von der ObservableCollection das Event Collection_Changed abonnieren und in diesem dann RaisePropertyChanged("CanCloseAllTabPages") packen. Damit hier immer bei einer änderung der Collection der Getter von CanCloseAllTabPages durchlaufen wird.

    Grüße
    Sascha
    PS: CanCloseAllTabPages sollte ja auch ein ReadOnly Property sein oder?

    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Ahhhhh... ich dämel, ja klar... das bekommt das ja garnet mit... auweia...

    Nofear23m schrieb:

    PS: CanCloseAllTabPages sollte ja auch ein ReadOnly Property sein oder?


    Genau das tut die Kurzschreibweise, weil das Property dadurch nur einen getter hat und keinen Setter.

    Danke Dir, ich probiers mal und melde mich wieder
    "Hier könnte Ihre Werbung stehen..."
    Achja, wegen deinem Problem zu dem Style der nicht übernommen wird.

    Mach oben im Style mal ein <Setter Property="IsEnabled" Value="True"/> rein.

    Grüße
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hi,
    das hatte ich auch schon versucht. Wenn der Button gedrückt wird, weird er kurz grau (als disabled) und bekommt dann direkt wieder die Standardfareb, bleibt aber disabled.
    Siehe Screenshots (falls man das da erkennen kann)
    Nach dem Start: (ist etwas dunkler weil da das DimmableOverlay gegriffen hat weil das Fenster deaktiviert war))
    Neue TabPage hinzugefügt = Button Enabled:

    Button gedrpckt = Alle TabPages werden geschloßen: Button is disabled, aber dennoch die DefaultFarbe wie die anderen Button
    "Hier könnte Ihre Werbung stehen..."
    Ja, er bekommt die Farbe der Animation beim MouseLeave. Da diese 300ms dauert wird die {StaticResource DefaultMain} gesetzt nachdem der PropertyTrigger für IsEnabled greift.

    Zumindest so wie ich das jetzt sehe ohne es zu probieren.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hmm, jetzt hab ich den Trigger mal auf 0:0:0.1 gesetzt, aber es scheint als würde er, wenn der Button gedrückt wurde, direkt auf die DefaultMain Farbe gehen, der Button ist aber enabled=False... das ist doch blöd...

    EDIT: wenn ich alle Tabs einzeln schließe (über den Button in der TabPage selbst), dann verhält sich der Button genauso, wie er soll.
    Wenn ich allerdings den Button drücke um alle Pages zu schließen, klappt es nicht.

    Ich hab auch die Methode ClearAllTabPages umbenannt und anstatt eine neue TabCollection zu erstellen, hab ich die TabPageCollection geleert:

    C#-Quellcode

    1. private void CloseAllTabs()
    2. {
    3. TabPages.Clear();
    4. }



    "Hier könnte Ihre Werbung stehen..."

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „MichaHo“ ()

    OK, zeig mal deinen Code im Collection_Changed, mal sehen was du da jetzt genau machst.

    Edit: @MichaHo
    Wenn du auf den Button drückst greift ja auch der Trigger. Beim einzelnen schliessen aber nicht. Weil dann das MouseLeave nicht eintritt.
    Dein Konzept für den Button-Style ist falsch.

    Beschreibe mal bitte ganz genau was der button wann machen soll, dann kann ich dir ein Beispiel erstellen.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hi,
    also im Collection_Changed ruf ich nur OnPropertyChanged auf

    C#-Quellcode

    1. TabPages.CollectionChanged += TabPages_CollectionChanged;
    2. private void TabPages_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
    3. {
    4. OnPropertyChanged(nameof(CanCloseAllTabPages));
    5. }


    Der Button soll im Grunde 3 Farben haben.
    ForegroundDefaultBrush = wenn Enabled und keine Maus drauf
    CharLightBlue = wenn Maus drauf
    ForegroundLightBrush = wenn disabled

    Ich habs nun mal mit nem anderen Style versucht (ein IconGrowButton), dort klappt es wie gewollt.

    Spoiler anzeigen

    XML-Quellcode

    1. <Style x:Key="IconGrowButton" TargetType="{x:Type Button}" BasedOn="{StaticResource BaseStyle}">
    2. <Setter Property="WindowChrome.IsHitTestVisibleInChrome" Value="True" />
    3. <Setter Property="Background" Value="Transparent" />
    4. <Setter Property="Foreground" Value="{StaticResource ForegroundDefaultBrush}" />
    5. <Setter Property="BorderThickness" Value="0" />
    6. <Setter Property="FontSize" Value="{StaticResource FontSizeXXLarge}" />
    7. <Setter Property="FontFamily" Value="{StaticResource FontAwesome2}" />
    8. <Setter Property="Padding" Value="30" />
    9. <Setter Property="Margin" Value="0" />
    10. <Setter Property="Width" Value="{Binding ActualHeight, RelativeSource={RelativeSource Self}}" />
    11. <Setter Property="Template">
    12. <Setter.Value>
    13. <ControlTemplate TargetType="{x:Type ButtonBase}">
    14. <Border x:Name="border"
    15. BorderBrush="{TemplateBinding BorderBrush}"
    16. BorderThickness="{TemplateBinding BorderThickness}"
    17. Background="{TemplateBinding Background}"
    18. SnapsToDevicePixels="True">
    19. <!-- Add a render scale transform -->
    20. <Border.RenderTransform>
    21. <ScaleTransform />
    22. </Border.RenderTransform>
    23. <Border.RenderTransformOrigin>
    24. <Point X="0.5" Y="0.5" />
    25. </Border.RenderTransformOrigin>
    26. <Grid>
    27. <Viewbox>
    28. <TextBlock x:Name="txt" Text="{TemplateBinding Content}"
    29. Focusable="False"
    30. FontFamily="{TemplateBinding FontFamily}"
    31. FontSize="{TemplateBinding FontSize}"
    32. HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
    33. Margin="{TemplateBinding Padding}"
    34. SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
    35. VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
    36. </Viewbox>
    37. </Grid>
    38. </Border>
    39. <ControlTemplate.Triggers>
    40. <EventTrigger RoutedEvent="MouseEnter">
    41. <BeginStoryboard>
    42. <Storyboard>
    43. <DoubleAnimation To="1.4" Duration="0:0:0.15" Storyboard.TargetName="border" Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)" />
    44. <DoubleAnimation To="1.4" Duration="0:0:0.15" Storyboard.TargetName="border" Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)" />
    45. </Storyboard>
    46. </BeginStoryboard>
    47. </EventTrigger>
    48. <EventTrigger RoutedEvent="MouseLeave">
    49. <BeginStoryboard>
    50. <Storyboard>
    51. <DoubleAnimation To="1" Duration="0:0:0.15" Storyboard.TargetName="border" Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleX)" />
    52. <DoubleAnimation To="1" Duration="0:0:0.15" Storyboard.TargetName="border" Storyboard.TargetProperty="(RenderTransform).(ScaleTransform.ScaleY)" />
    53. </Storyboard>
    54. </BeginStoryboard>
    55. </EventTrigger>
    56. <Trigger Property="IsEnabled" Value="False">
    57. <Setter Property="Background" TargetName="border" Value="Transparent"/>
    58. <Setter Property="Foreground" TargetName="txt" Value="{StaticResource ForegroundLightBrush}"/>
    59. </Trigger>
    60. </ControlTemplate.Triggers>
    61. </ControlTemplate>
    62. </Setter.Value>
    63. </Setter>
    64. </Style>

    "Hier könnte Ihre Werbung stehen..."
    Dein problem ist das du in deiner Animation immer die Foreground überschreibst.

    Wenn der button sich selbst deaktiviert passiert dies eben. Machst du es mit der Tastatur vermutlich nicht weil hier kein MouseEnter oder MouseLeave eintritt.

    Ich würde dir empfehlen die Trigger wie im originalem Template zu implementieren. Wenn ich jetzt keinen Knoten im Hirn habe denke ich das du mit animatinen in diesem speziellen Fall mit MultiTriggern arbeiten müsstest und in diesem je nach Button-State und dem Properte "IsDefaulted" arbeiten musst.

    XML-Quellcode

    1. <ControlTemplate.Triggers>
    2. <Trigger Property="IsDefaulted" Value="true">
    3. <Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
    4. </Trigger>
    5. <Trigger Property="IsMouseOver" Value="true">
    6. <Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
    7. <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
    8. </Trigger>
    9. <Trigger Property="IsPressed" Value="true">
    10. <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
    11. <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
    12. </Trigger>
    13. <Trigger Property="IsEnabled" Value="false">
    14. <Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
    15. <Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
    16. <Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
    17. </Trigger>
    18. </ControlTemplate.Triggers>


    PS: Wenn es denn mit Animationen sein soll kann ich versuchen ein Beispiel zu erstellen, da muss ich aber selbst erstmal rumprobieren.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hi,

    Alles Gut, der HeaderIconButton Style ist nicht so wichtig, hab nun den IconGrowButton genommen, das sieht auch gut aus und klappt perfekt.
    Nun geh ich zur nächsten Baustelle :) Dem gerade selektierten Ordner im TreeView ein neues File hinzufügen....

    Danke Dir bis hierher, Thread ist damit erstmal erledigt.
    "Hier könnte Ihre Werbung stehen..."

    MichaHo schrieb:

    Dem gerade selektierten Ordner im TreeView ein neues File hinzufügen....

    Tipp: Da brauchste im VM ein Property IsSelected. :D

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##