DependencyProperty feuert PropertyChangedCallback nicht

  • WPF

Es gibt 4 Antworten in diesem Thema. Der letzte Beitrag () ist von Trade.

    DependencyProperty feuert PropertyChangedCallback nicht

    Moin,

    ich versuche gerade, einen Wizard mit ViewModels, Views und UserControls zu basteln.
    Dazu habe ich eine ViewModel-Basisklasse PagedWindowViewModel für alle Fenster geschrieben, die verschiedene Inhalte (in Form von UserControls) anzeigen können. Das ist also die Basisfunktionalität für den Wizard, bei dem man die verschiedenen Teile durchlaufen kann.
    Ein solcher Dialog ist dann bspw. das NewProjectWindow:


    PagedWindowViewModel.cs

    C#-Quellcode

    1. // Author: Dominic Beger (Trade/ProgTrade) 2017
    2. using System;
    3. using System.Collections.Generic;
    4. using System.Collections.ObjectModel;
    5. using System.Linq;
    6. using System.Windows;
    7. using System.Windows.Data;
    8. using nUpdate.Administration.Infrastructure;
    9. namespace nUpdate.Administration.ViewModels
    10. {
    11. public abstract class PagedWindowViewModel : ViewModel
    12. {
    13. private static readonly DependencyProperty PageCanGoBackProperty =
    14. DependencyProperty.Register("PageCanGoBack", typeof(bool), typeof(PagedWindowViewModel),
    15. new PropertyMetadata((obj, e) =>
    16. {
    17. ((PagedWindowViewModel)obj).PageNavigationPropertiesChanged(obj, e);
    18. }));
    19. private static readonly DependencyProperty PageCanGoForwardProperty =
    20. DependencyProperty.Register("PageCanGoForward", typeof(bool), typeof(PagedWindowViewModel),
    21. new PropertyMetadata((obj, e) =>
    22. {
    23. ((PagedWindowViewModel)obj).PageNavigationPropertiesChanged(obj, e);
    24. }));
    25. private static readonly DependencyPropertyKey CanGoBackPropertyKey =
    26. DependencyProperty.RegisterReadOnly("CanGoBack", typeof(bool), typeof(PagedWindowViewModel),
    27. new PropertyMetadata(false));
    28. /// <summary>
    29. /// Identifies the <see cref="CanGoBack" /> dependency property.
    30. /// </summary>
    31. public static readonly DependencyProperty CanGoBackProperty = CanGoBackPropertyKey.DependencyProperty;
    32. private static readonly DependencyPropertyKey CanGoForwardPropertyKey =
    33. DependencyProperty.RegisterReadOnly("CanGoForward", typeof(bool), typeof(PagedWindowViewModel),
    34. new PropertyMetadata(false));
    35. /// <summary>
    36. /// Identifies the <see cref="CanGoForward" /> dependency property.
    37. /// </summary>
    38. public static readonly DependencyProperty CanGoForwardProperty = CanGoForwardPropertyKey.DependencyProperty;
    39. private PageViewModel _currentPageViewModel;
    40. private RelayCommand _goBackCommand;
    41. private RelayCommand _goForwardCommand;
    42. private ReadOnlyCollection<PageViewModel> _pageViewModels;
    43. /// <summary>
    44. /// Gets a value indicating whether the user is allowed to go back.
    45. /// </summary>
    46. public bool CanGoBack => (bool) GetValue(CanGoBackProperty);
    47. /// <summary>
    48. /// Gets a value indicating whether the current view model allows to go forward.
    49. /// </summary>
    50. public bool CanGoForward => (bool) GetValue(CanGoForwardProperty);
    51. /// <summary>
    52. /// Gets or sets the view model of the current page to be shown.
    53. /// </summary>
    54. public PageViewModel CurrentPageViewModel
    55. {
    56. get
    57. {
    58. EnsureObjectState();
    59. return _currentPageViewModel;
    60. }
    61. set
    62. {
    63. EnsureObjectState();
    64. SetProperty(value, ref _currentPageViewModel);
    65. SetPageBindings();
    66. }
    67. }
    68. /// <summary>
    69. /// Gets the command for going back to the previous page.
    70. /// </summary>
    71. public RelayCommand GoBackCommand
    72. {
    73. get
    74. {
    75. EnsureObjectState();
    76. return _goBackCommand ?? (_goBackCommand = new RelayCommand(() =>
    77. {
    78. if (CanGoBack)
    79. CurrentPageViewModel = PageViewModels[PageViewModels.IndexOf(CurrentPageViewModel) - 1];
    80. }));
    81. }
    82. }
    83. /// <summary>
    84. /// Gets the command for going forward to the next page.
    85. /// </summary>
    86. public RelayCommand GoForwardCommand
    87. {
    88. get
    89. {
    90. EnsureObjectState();
    91. return _goForwardCommand ?? (_goForwardCommand = new RelayCommand(() =>
    92. {
    93. if (CanGoForward)
    94. CurrentPageViewModel = PageViewModels[PageViewModels.IndexOf(CurrentPageViewModel) + 1];
    95. }));
    96. }
    97. }
    98. /// <summary>
    99. /// Gets the view models of the pages to be shown.
    100. /// </summary>
    101. protected ReadOnlyCollection<PageViewModel> PageViewModels
    102. {
    103. get
    104. {
    105. EnsureObjectState();
    106. return _pageViewModels;
    107. }
    108. private set { _pageViewModels = value; }
    109. }
    110. private void EnsureObjectState()
    111. {
    112. if (_pageViewModels == null)
    113. throw new InvalidOperationException("Call InitializePages before working with this object.");
    114. if (!_pageViewModels.Any())
    115. throw new InvalidOperationException("There are no page view models available.");
    116. }
    117. /// <summary>
    118. /// Initialize the pages associated with this window.
    119. /// </summary>
    120. protected void InitializePages(IList<PageViewModel> viewModels)
    121. {
    122. PageViewModels = new ReadOnlyCollection<PageViewModel>(viewModels);
    123. CurrentPageViewModel = PageViewModels[0];
    124. }
    125. private void PageNavigationPropertiesChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    126. {
    127. var isAtBegin = PageViewModels.IndexOf(CurrentPageViewModel) == 0;
    128. SetValue(CanGoBackPropertyKey, !isAtBegin && (bool)GetValue(PageCanGoBackProperty));
    129. var isAtEnd = PageViewModels.IndexOf(CurrentPageViewModel) == PageViewModels.Count - 1;
    130. SetValue(CanGoForwardPropertyKey, !isAtEnd && (bool)GetValue(PageCanGoForwardProperty));
    131. }
    132. private void SetPageBindings()
    133. {
    134. var canGoBackwardBinding = new Binding
    135. {
    136. Source = CurrentPageViewModel,
    137. Path = new PropertyPath("CanGoBack"),
    138. Mode = BindingMode.OneWay,
    139. UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
    140. };
    141. BindingOperations.ClearBinding(this, PageCanGoBackProperty);
    142. BindingOperations.SetBinding(this, PageCanGoBackProperty, canGoBackwardBinding);
    143. var canGoForwardBinding = new Binding
    144. {
    145. Source = CurrentPageViewModel,
    146. Path = new PropertyPath("CanGoForward"),
    147. Mode = BindingMode.OneWay,
    148. UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged
    149. };
    150. BindingOperations.ClearBinding(this, PageCanGoForwardProperty);
    151. BindingOperations.SetBinding(this, PageCanGoForwardProperty, canGoForwardBinding);
    152. }
    153. }
    154. }


    Der Inhalt wird über ein ContentControl verwaltet, dem ich als Inhalt ein ViewModel für das passende UserControl gebe und dann wird über eine spezielle Unterklasse das passende View über einen IValueConverter gesucht (s. Zeile 25 im XAML).

    NewProjectWindow.xaml

    XML-Quellcode

    1. <Window x:Class="nUpdate.Administration.Views.NewProjectWindow"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    5. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    6. xmlns:newProjectViewModels="clr-namespace:nUpdate.Administration.ViewModels.NewProject"
    7. xmlns:viewModels="clr-namespace:nUpdate.Administration.ViewModels"
    8. Title="New project - nUpdate Administration v4.0"
    9. Width="520"
    10. Height="303.333"
    11. Icon="/./Icons/nUpdate.ico"
    12. SizeToContent="Height"
    13. WindowStartupLocation="CenterOwner"
    14. mc:Ignorable="d">
    15. <Window.DataContext>
    16. <viewModels:NewProjectViewModel />
    17. </Window.DataContext>
    18. <Window.Resources>
    19. <newProjectViewModels:NewProjectViewManager x:Key="NewProjectViewManager" />
    20. <SolidColorBrush x:Key="BottomPanelBrush" Color="#F0F0F0" />
    21. <SolidColorBrush x:Key="BottomPanelLineBrush" Color="DarkGray" />
    22. </Window.Resources>
    23. <DockPanel>
    24. <ContentControl Height="230"
    25. Content="{Binding CurrentPageViewModel,
    26. Converter={StaticResource NewProjectViewManager}}"
    27. DockPanel.Dock="Top" />
    28. <StackPanel DockPanel.Dock="Bottom" Orientation="Vertical">
    29. <Rectangle Height="1"
    30. VerticalAlignment="Center"
    31. Fill="{StaticResource BottomPanelLineBrush}"
    32. SnapsToDevicePixels="True" />
    33. <Grid Background="{StaticResource BottomPanelBrush}">
    34. <StackPanel Margin="10"
    35. HorizontalAlignment="Right"
    36. VerticalAlignment="Center"
    37. Orientation="Horizontal">
    38. <Button MinWidth="92"
    39. Command="{Binding GoBackCommand}"
    40. Content="Back"
    41. IsEnabled="{Binding CanGoBack}" />
    42. <Button MinWidth="92"
    43. Margin="10,0"
    44. Command="{Binding GoForwardCommand}"
    45. Content="Forward"
    46. IsEnabled="{Binding CanGoForward}" />
    47. <Button MinWidth="92"
    48. Command="{Binding CancelCommand}"
    49. Content="Cancel"
    50. IsCancel="True" />
    51. </StackPanel>
    52. </Grid>
    53. </StackPanel>
    54. </DockPanel>
    55. </Window>


    ViewManager.cs

    C#-Quellcode

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Globalization;
    4. using System.Windows;
    5. using System.Windows.Data;
    6. namespace nUpdate.Administration.ViewModels
    7. {
    8. /// <summary>
    9. /// Provides an abstract view manager class that offers basic convertion methods for finding the corresponding view for a specified view model by using an <see cref="IValueConverter"/>.
    10. /// </summary>
    11. public abstract class ViewManager : IValueConverter
    12. {
    13. /// <summary>
    14. /// Saves the types of the view models and their corresponding views.
    15. /// </summary>
    16. private readonly Dictionary<Type, Type> _viewTypes = new Dictionary<Type, Type>();
    17. /// <summary>
    18. /// Saves the types of the view models and an instance of their view.
    19. /// </summary>
    20. private readonly Dictionary<Type, FrameworkElement> _views = new Dictionary<Type, FrameworkElement>();
    21. protected ViewManager()
    22. { }
    23. protected ViewManager(Dictionary<Type, Type> viewTypes)
    24. {
    25. AddViewTypes(viewTypes);
    26. }
    27. protected ViewManager(Dictionary<Type, FrameworkElement> views)
    28. {
    29. AddViews(views);
    30. }
    31. protected void AddViewType(Type viewModelType, Type viewType)
    32. {
    33. _viewTypes.Add(viewModelType, viewType);
    34. }
    35. protected void AddViewTypes(Dictionary<Type, Type> viewTypes)
    36. {
    37. foreach (var entry in viewTypes)
    38. {
    39. AddViewType(entry.Key, entry.Value);
    40. }
    41. }
    42. protected void AddView(Type viewModelType, FrameworkElement view)
    43. {
    44. AddViewType(viewModelType, view.GetType());
    45. _views.Add(viewModelType, view);
    46. }
    47. protected void AddViews(Dictionary<Type, FrameworkElement> views)
    48. {
    49. foreach (var entry in views)
    50. {
    51. AddView(entry.Key, entry.Value);
    52. }
    53. }
    54. /// <summary>
    55. /// Gets an instance of the view that corresponds to the specified view model type.
    56. /// </summary>
    57. /// <param name="viewModelType">The type of the view model.</param>
    58. /// <param name="viewModelInstance">An existing instance of the view model.</param>
    59. /// <param name="viewType">The type of the view.</param>
    60. /// <returns>An instance of the corresponding view.</returns>
    61. private object GetViewInstance(Type viewModelType, object viewModelInstance, Type viewType)
    62. {
    63. // There is already a cached instance available.
    64. if (_views.ContainsKey(viewModelType))
    65. return _views[viewModelType];
    66. // No instance, yet. Let's create one and set the view model instance as data context.
    67. var view = (FrameworkElement) Activator.CreateInstance(viewType);
    68. view.DataContext = viewModelInstance;
    69. _views.Add(viewModelType, view);
    70. return view;
    71. }
    72. public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    73. {
    74. if (value == null)
    75. throw new ArgumentNullException(nameof(value));
    76. var viewModelType = value.GetType();
    77. return !_viewTypes.ContainsKey(viewModelType) ? null : GetViewInstance(viewModelType, value, _viewTypes[viewModelType]);
    78. }
    79. public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    80. {
    81. // This is not used in any case, so just leave it empty and throw a NotImplementedException.
    82. throw new NotImplementedException();
    83. }
    84. }
    85. }



    NewProjectViewManager.cs

    C#-Quellcode

    1. using System;
    2. using System.Collections.Generic;
    3. using nUpdate.Administration.Views.NewProject;
    4. namespace nUpdate.Administration.ViewModels.NewProject
    5. {
    6. public class NewProjectViewManager : ViewManager
    7. {
    8. public NewProjectViewManager() : base(
    9. new Dictionary<Type, Type>
    10. {
    11. {typeof(GenerateKeyPairPageViewModel), typeof(GenerateKeyPairPage)}
    12. })
    13. { }
    14. }
    15. }


    Die Architektur ist so aufgebaut, weil ich im Hintergrund die Daten bereithalten und verarbeiten muss und somit gehe ich von den jeweiligen ViewModels aus, damit ich dann das entsprechende View anzeigen kann.
    Im PagedWindowViewModel seht Ihr die Methode InitializePages, die von der erbenden Klasse dann aufgerufen wird und der dann die ganzen Pages (, die ja eigentlich UserControls sind) übergeben werden.
    Beispiel dann:

    C#-Quellcode

    1. using System.Collections.Generic;
    2. using nUpdate.Administration.ViewModels.NewProject;
    3. namespace nUpdate.Administration.ViewModels
    4. {
    5. public class NewProjectViewModel : PagedWindowViewModel
    6. {
    7. public ProjectCreationData ProjectCreationData { get; } = new ProjectCreationData();
    8. public NewProjectViewModel()
    9. {
    10. InitializePages(new List<PageViewModel>
    11. {
    12. new GenerateKeyPairPageViewModel(this),
    13. new GenerateKeyPairPageViewModel(this),
    14. new GenerateKeyPairPageViewModel(this)
    15. });
    16. }
    17. }
    18. }


    Da ist jetzt zu Testzwecken drei mal die selbe Page drin, damit die Klasse PagedWindowViewModel das Vorgehen auch zulässt, weil rechts weitere Pages kommen. Da wären wir jetzt auch schon beim Problem, das ich habe.
    Die einzelnen Pages erben von PageViewModel und geben nur an, ob sie es erlauben, vor und zurück zu gehen. Etwa dann, wenn alle Daten, die man eingegeben hat, in Ordnung sind, kann man weiter.

    C#-Quellcode

    1. namespace nUpdate.Administration.ViewModels
    2. {
    3. /// <summary>
    4. /// Represents a view model for a wizard page.
    5. /// </summary>
    6. public abstract class PageViewModel : ViewModel
    7. {
    8. private bool _canGoForward;
    9. public bool CanGoForward
    10. {
    11. get { return _canGoForward; }
    12. set { SetProperty(value, ref _canGoForward); }
    13. }
    14. private bool _canGoBack;
    15. public bool CanGoBack
    16. {
    17. get { return _canGoBack; }
    18. set { SetProperty(value, ref _canGoBack); }
    19. }
    20. }
    21. }


    Die Klasse PagedWindowViewModel bzw. deren Unterklassen verwaltet ja die ganzen Pages in PageViewModels und auch die aktuelle in CurrentPageViewModel. Insofern die aktuelle Page dann ihr "OK" gibt, dass man weiter kann, muss das PagedWindowViewModel dann noch prüfen, ob überhaupt eine Page vorhanden ist, zu der gewechselt werden kann. Daher die Unterscheidung mit den DependencyProperties: PageCanGoBackProperty und PageCanGoForwardProperty stehen eben für den Status der aktuellen Page. Hier ist mein Problem. Sobald ich in der Page bspw. CanGoForward setze, sollte über das Binding, das ich in SetPageBindings setze, die DependencyProperty das mitbekommen und dann das PropertyChangedCallback aufrufen. Aber genau das passiert nicht.
    Nehmen wir nun das Beispiel der GenerateKeyPairPage, die oben im Bild zu sehen ist:

    GenerateKeyPairPageViewModel.cs

    C#-Quellcode

    1. using System.Threading.Tasks;
    2. using System.Windows.Input;
    3. using nUpdate.Administration.Infrastructure;
    4. namespace nUpdate.Administration.ViewModels.NewProject
    5. {
    6. public class GenerateKeyPairPageViewModel : PageViewModel
    7. {
    8. private readonly NewProjectViewModel _newProjectViewModel;
    9. private ICommand _loadCommand;
    10. public GenerateKeyPairPageViewModel(NewProjectViewModel viewModel)
    11. {
    12. _newProjectViewModel = viewModel;
    13. LoadCommand = new RelayCommand(() => GenerateKeyPair());
    14. }
    15. public ICommand LoadCommand
    16. {
    17. get { return _loadCommand; }
    18. set
    19. {
    20. _loadCommand = value;
    21. OnPropertyChanged();
    22. }
    23. }
    24. public Task GenerateKeyPair()
    25. {
    26. return Task.Run(async () =>
    27. {
    28. /*var rsa = new RsaManager();
    29. _newProjectViewModel.ProjectCreationData.Project.PublicKey = rsa.PublicKey;
    30. _newProjectViewModel.ProjectCreationData.PrivateKey = rsa.PrivateKey;*/
    31. await Task.Delay(5000);
    32. CanGoForward = true;
    33. });
    34. }
    35. }
    36. }


    GenerateKeyPairPage.xaml

    XML-Quellcode

    1. <UserControl x:Class="nUpdate.Administration.Views.NewProject.GenerateKeyPairPage"
    2. xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    3. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    4. xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    5. xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    6. xmlns:loadingIndicators="clr-namespace:LoadingIndicators.WPF;assembly=LoadingIndicators.WPF"
    7. xmlns:local="clr-namespace:nUpdate.Administration.Views.NewProject"
    8. xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    9. d:DesignHeight="230"
    10. d:DesignWidth="520"
    11. Background="White"
    12. mc:Ignorable="d">
    13. <i:Interaction.Triggers>
    14. <i:EventTrigger EventName="Loaded">
    15. <i:InvokeCommandAction Command="{Binding LoadCommand}" />
    16. </i:EventTrigger>
    17. </i:Interaction.Triggers>
    18. <DockPanel Margin="20">
    19. <TextBlock DockPanel.Dock="Top"
    20. FontSize="12pt"
    21. Foreground="#003399"
    22. Text="Key Pair Generation" />
    23. <StackPanel VerticalAlignment="Center" Orientation="Horizontal">
    24. <loadingIndicators:LoadingIndicator Foreground="LightGray"
    25. SpeedRatio="1"
    26. Style="{DynamicResource LoadingIndicatorArcsStyle}">
    27. <loadingIndicators:LoadingIndicator.LayoutTransform>
    28. <ScaleTransform ScaleX="0.65" ScaleY="0.65" />
    29. </loadingIndicators:LoadingIndicator.LayoutTransform>
    30. </loadingIndicators:LoadingIndicator>
    31. <TextBlock Margin="15,0,0,0"
    32. VerticalAlignment="Center"
    33. Text="Please wait while the keys are generated. This may take some minutes..."
    34. TextWrapping="Wrap" />
    35. </StackPanel>
    36. </DockPanel>
    37. </UserControl>


    Problem:

    Hier setze ich ganz klar CanGoForward auf true. Habe dort einen Haltepunkt gesetzt und das ganze wird ausgeführt. In dem Moment müsste eigentlich über das Binding die DependencyProperty das mitbekommen und das Callback aufrufen. Allerdings führt dort überhaupt nichts aus und ich kann mit der Änderung nichts anfangen. Was dann eigentlich geschehen sollte, ist, dass ich das weitergebe und noch prüfe, ob weitere Pages in der Collection da sind, zu denen ich wechseln kann. Und dann sollten die Properties CanGoForward und CanGoBack gesetzt werden, damit die View die Buttons en- oder disablen kann.
    Warum wird das Callback nicht aufgerufen? Damit meine ich das in Codebeispiel 1 in Zeile 19 zum Beispiel.

    Hier übrigens die anderen Klassen, die vererbt werden. Nichts besonderes, sondern eigentlich nur die Implementierung von INotifyPropertyChanged als Basisklasse, welche ein DependencyObject ist (logischerweise).

    C#-Quellcode

    1. using nUpdate.Administration.Infrastructure;
    2. namespace nUpdate.Administration.ViewModels
    3. {
    4. public class ViewModel : Model
    5. { }
    6. }


    C#-Quellcode

    1. using System;
    2. using System.ComponentModel;
    3. using System.Linq.Expressions;
    4. using System.Reflection;
    5. using System.Runtime.CompilerServices;
    6. using System.Windows;
    7. namespace nUpdate.Administration.Infrastructure
    8. {
    9. [Serializable]
    10. public class Model : DependencyObject, INotifyPropertyChanged
    11. {
    12. public event PropertyChangedEventHandler PropertyChanged;
    13. protected virtual bool SetProperty<T>(T value, ref T field, Expression<Func<object>> property)
    14. {
    15. return SetProperty(value, ref field);
    16. }
    17. protected virtual bool SetProperty<T>(T value, ref T field, [CallerMemberName]string propertyName = null)
    18. {
    19. if (field != null && field.Equals(value))
    20. return false;
    21. field = value;
    22. OnPropertyChanged();
    23. return true;
    24. }
    25. public void OnPropertyChanged([CallerMemberName] string propertyName = null)
    26. {
    27. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    28. }
    29. public void OnPropertyChanged(Expression<Func<object>> property)
    30. {
    31. OnPropertyChanged();
    32. }
    33. protected string GetPropertyName(Expression<Func<object>> property)
    34. {
    35. var lambda = property as LambdaExpression;
    36. MemberExpression memberExpression;
    37. var body = lambda.Body as UnaryExpression;
    38. if (body != null)
    39. {
    40. var unaryExpression = body;
    41. memberExpression = unaryExpression.Operand as MemberExpression;
    42. }
    43. else
    44. {
    45. memberExpression = lambda.Body as MemberExpression;
    46. }
    47. if (memberExpression == null)
    48. return string.Empty;
    49. var propertyInfo = memberExpression.Member as PropertyInfo;
    50. return propertyInfo != null ? propertyInfo.Name : string.Empty;
    51. }
    52. }
    53. }


    Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    Hat niemand eine Idee? :rolleyes: Habe schon alles ausprobiert und debuggt, aber kam nicht darauf, warum das nicht gefeuert wird.

    Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    Hi fichz und danke für Deine Antwort,

    asynchron muss das Binding in dem Sinne ja nicht sein, da die Daten nicht so lange zum Abrufen brauchen, dass das UI blockiert würde. Hab's mal getestet, aber bringt daher, wie vermutet, auch keine Änderung. Jedoch hatte ich gerade den Gedanken, dass es was mit dem asynchronen Task zu tun haben könnte. Allerdings passiert auch bei einer asynchronen Prozedur/Methode genau dasselbe. Also daran liegt es leider (oder auch Gott sei Dank) nicht.

    Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    Problem ist gelöst. Der Fehler lag in der Klasse Model im optionalen Parameter von OnPropertyChanged und damit dem CallerMemberAttribute.
    Hier wurde dann SetProperty übergeben, was natürlich nicht der Name der Property ist, die ich setzen wollte. Also einfach den propertyName explizit übergeben und es funktioniert wunderbar. :)
    Habe die Klasse eh noch ein wenig aufgeräumt und Sachen entfernt, die ich nicht benutze oder die nur Verwirrung stiften.

    C#-Quellcode

    1. using System;
    2. using System.ComponentModel;
    3. using System.Threading;
    4. using System.Windows;
    5. namespace nUpdate.Administration.Infrastructure
    6. {
    7. [Serializable]
    8. public class Model : DependencyObject, INotifyPropertyChanged
    9. {
    10. public event PropertyChangedEventHandler PropertyChanged;
    11. protected virtual bool SetProperty<T>(T value, ref T field, string propertyName = null)
    12. {
    13. // ReSharper disable once CompareNonConstrainedGenericWithNull
    14. if (field != null && field.Equals(value))
    15. return false;
    16. field = value;
    17. OnPropertyChanged(propertyName);
    18. return true;
    19. }
    20. public void OnPropertyChanged(string propertyName = null)
    21. {
    22. if (Application.Current.Dispatcher.Thread != Thread.CurrentThread)
    23. Application.Current.Dispatcher.Invoke(() => OnPropertyChanged(propertyName));
    24. else
    25. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    26. }
    27. }
    28. }


    Ist jetzt eben sehr einfach gehalten und auch PropertyChanged auf Threadebene sollte jetzt ohne Probleme klappen. Ansonsten funktioniert der Code wie gewünscht. Habe auch noch einige Änderungen vorgenommen, was die RelayCommands angeht. So nutze ich jetzt die Überladung mit canExecute und gebe einfach als Func ein Lambda (z. B. () => CanGoBack) an. Das sorgt dafür, dass ich mir die Abfrage sparen kann und gleichzeitig kein Binding von IsEnabled mehr an CanGoBack oder CanGoForward benötige, da es jetzt automatisch klappt.

    Grüße
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!: