PostSharp NotifyPropertyChanged manual raised event - postsharp

I have simple situation here. But NotifyPropertyChanged event is not raised even when I use viewmodel.Test = true. Is there some solution how to do that? Can I use some method to raise event manually? Somethin like OnPropertyChange(nameof(Test))? And is there some way how to track change in config too?
//From viewmodel with aspect NotifyPropertyChanged
private readonly IConfig config;
public bool Test
{
get { return config.Test; }
internal set
{
config.Test = value;
}
}
public class Config : IConfig
{
public bool Test { get; set; }
}

The Config class must have NotifyPropertyChanged aspect too.
http://support.sharpcrafters.com/discussions/problems/1862-notifypropertychanged-aspect-does-not-raise-events-for-proxied-objects-that-do-not-implement-inpc

Related

Maintain User Control State in UWP Application using Template 10

I am creating UWP app using Template 10. I have created user control like this.
<my:DeviceInfoUserControl OnEndpointTypeChange="{Binding OnEndpointTypeChangeCommand}" Component="{Binding DeviceManagementViewModel,Mode=TwoWay}"></my:DeviceInfoUserControl>
I have Radio Buttons on User Control. I have added User Control on Multiple screens.
This user control has its own ViewModel as well as Some Dependency Properties as follows:
public class DeviceManagementViewModel : ViewModelBase
{
}
public sealed partial class DeviceInfoUserControl : UserControl
{
public bool IsToggled = true;
public DeviceInfoUserControl()
{
this.InitializeComponent();
}
public static readonly DependencyProperty OnEndpointTypeChangeProperty =
DependencyProperty.Register(
"OnEndpointTypeChange",
typeof(ICommand),
typeof(DeviceInfoUserControl), new PropertyMetadata(null));
public ICommand OnEndpointTypeChange
{
get { return (ICommand)GetValue(OnEndpointTypeChangeProperty); }
set { SetValue(OnEndpointTypeChangeProperty, value); }
}
public static readonly DependencyProperty ComponentProperty = DependencyProperty.Register("Component", typeof(DeviceManagementViewModel), typeof(DeviceInfoUserControl), new PropertyMetadata(null));
public DeviceManagementViewModel Component
{
get { return (DeviceManagementViewModel)GetValue(ComponentProperty); }
set { SetValue(ComponentProperty, value); }
}
}
I want to preserve Radio Button Selection across all screens. How should I achieve this?
You have to ensure that the same ViewModel instance is used for all control instance. The XAML way is always create new instance:
<Page.DataContext>
<vm:DetailPageViewModel x:Name="ViewModel" />
</Page.DataContext>
In the Template10's Bootstrapper class with the ResolveForPage method override, you can inject ViewModel's after the page navigation through a custom logic, or through dependency injection LINK
Don't know its better way or not but I have achieved this by making Singletone Viewmodel.
public class DeviceManagementViewModel : ViewModelBase
{
public static readonly DeviceManagementViewModel _instance = new DeviceManagementViewModel ();
private DeviceManagementViewModel ()
{
}
/*Properties and Methods */
}
In Parent Screen ViewModel I have created following property
private DeviceManagementViewModel _deviceManagementViewModel;
public DeviceManagementViewModel DeviceManagementViewModel1
{
get { return _deviceManagementViewModel; }
set { Set(ref _deviceManagementViewModel, value); }
}
I have Instantiated property in Constructor:
public ConfigurationViewModel()
{
DeviceManagementViewModel1 = DeviceManagementViewModel._instance;
}
And on User Control:
<my:DeviceInfoUserControl OnEndpointTypeChange="{Binding OnEndpointTypeChangeCommand}" Component="{Binding DeviceManagementViewModel1,Mode=TwoWay}"></my:DeviceInfoUserControl>

Dependency property inside viewmodel in Prism

Is there any way to declare dependency property inside viewmodel? I want to declare a dependency property inside viewmodel and change it's value through command.
public class MyViewModel : Prism.Windows.Mvvm.ViewModelBase
{
public bool IsPaneVisible
{
get { return (bool)GetValue(IsPaneVisibleProperty); }
set { SetValue(IsPaneVisibleProperty, value); }
}
public static readonly DependencyProperty IsPaneVisibleProperty =
DependencyProperty.Register("IsPaneVisible", typeof(bool), typeof(MyViewModel), new PropertyMetadata(0));
public ICommand VisibilityChangeCommand { get; set; }
public MyViewModel()
{
VisibilityChangeCommand = new DelegateCommand(OnVisibilityChange);
}
private void OnVisibilityChange()
{
IsPaneVisible = !IsPaneVisible;
}
}
Problem is, I am getting some compilation error in IsPaneVisible' getter/setter : "GetValue does not exist in the current context". Is there any alternative way to do this?
A DependencyProperty is used on a DependencyObject, an example of this is a UserControl. Prism's ViewModelBase is no DependencyObject, mainly because this type is platform specific. To support binding from a viewmodel, we typically use INotifyPropertyChanged.
Prism implements this interface in the BindableBase base class, from which ViewModelBase derives as well. You define your properties like this:
private string _imagePath;
public string ImagePath
{
get { return _imagePath; }
set { SetProperty(ref _imagePath, value); }
}
If you install the Prism Template Pack Visual Studio extension, you can use the propp code snippet.

SerializedProperty avoiding getter?

I am using getters and setters for variables to avoid checking if they are null. Also I don't need Start() and Awake() functions in most cases.
public Button[] TitlebarButtons
{
get { return titlebar_buttons ?? (titlebar_buttons = GetComponentsInChildren<Button>()); }
}
private Button[] titlebar_buttons;
public Color CloseButtonNormalColor
{
get { return TitlebarButtons[2].colors.normalColor; }
set
{
ColorBlock temp = ColorBlock.defaultColorBlock;
temp.normalColor = value;
temp.highlightedColor = TitlebarButtons[2].colors.highlightedColor;
temp.pressedColor = TitlebarButtons[2].colors.pressedColor;
TitlebarButtons[2].colors = temp;
}
}
And in Editor script I am trying to make CloseButtonNormalColor a Serialized Property:
private Title Title;
private SerializedProperty close_button_normal_color;
void OnEnable()
{
Title = (Title)target;
close_button_normal_color = serializedObject.FindProperty("CloseButtonNormalColor");
}
But when I'm using it, NullReferenceExeption appears.
close_button_normal_color.colorValue = EditorGUILayout.ColorField("Normal", close_button_normal_color.colorValue);
Is there any solution for that?
Usually to just exposes properties on inspector you can use
[ShowProperty]
public MyProperty {get; set;}
and fields
[SerializeField]
private myField;
i found two "workarounds" for this.
One come from this thread in unity forum:
https://forum.unity.com/threads/using-property-fields-in-custom-editor-when-using-auto-properties.828597/
So, you can:
public class ClassToCustomEditor
{
[field:SerializeField] public int CustomEditorProperty { get; set; }
}
and then in your custom editor script:
void OnEnable()
{
IsCraftable = serializedObject.FindProperty(
string.Format("<{0}>k__BackingField", "IsCraftable")
);
}
for me, in Unity 2020.1.14f1 works like a charm. If you need more details, i suggest you to visit the thread above.
The other work around is create private fields (whose FindProperty find) and then create propeties who access and modify these fields.
Something like:
public class MyClassToCustomEditor
{
[SerializeField] private string itemName;
public string ItemName
{
get { return itemName; }
set { itemName = value; }
}
}
and then the FindProperty would be able to find it right the way you write it.

How to redirect ILoggerFacade to ViewModel?

I want write my logs to the ViewModel, so I can expose logs to the users.
First I bind View to ViewModel
<TextBox Grid.Row="1" TextWrapping="Wrap" Text="{Binding Logger}" AcceptsReturn="True" IsReadOnly="True"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
This is the ViewModel
private string logger;
public string Logger
{
get { return logger; }
set
{
logger = value;
this.RaisePropertyChanged("Logger");
}
}
Then, I create the customer logger class which implements ILoggerFacade,and override the CreateLogger method in Bootstrapper.
In Bootstrapper
protected override ILoggerFacade CreateLogger()
{
return new MainLogger();
}
In Customer Logger class
public class MainLogger : ILoggerFacade
{
public void Log(string message, Category category, Priority priority)
{
string messageToLog = String.Format(System.Globalization.CultureInfo.InvariantCulture, "{1}: {2}. Priority: {3}. Timestamp:{0:u}.", DateTime.Now, category.ToString().ToUpper(System.Globalization.CultureInfo.InvariantCulture), message, priority.ToString());
//////??????//////
}
}
And what should fill in ???????. I tried Import IEventAggregator to publish data to ViewModel, and directly import ViewModel here. Neither works, because CreatorLogger Method is called before container is registered. So how can I write logs to the ViewModel?
The logger should simply save the log message(s) and expose it in a property:
public interface IMainLogger : ILoggerFacade
{
List Messages { get; }
}
public class MainLogger : IMainLogger
{
public MainLogger()
{
Messages = new ObservableCollection<string>();
}
public ObservableCollection<string> Messages { get; private set; }
public void Log(string message, Category category, Priority priority)
{
string messageToLog = ...;
Messages.Add(messageToLog);
}
}
That's basically what a logger is supposed to do: Log messages. Now you want to display it inside some TextBox which is contained in a View that you inject into a region, right? To do that, you need to pass that logger to the Module of this region's view via constructor dependency injection. I am working with MEF, so I'm not too sure about how to do it with Unity, but it probably looks something like this, when you configure the container in code:
container.RegisterType<IMainLogger, MainLogger>(new ContainerControlledLifetimeManager());
container.RegisterType<ContainingTextBoxModule>(new InjectionConstructor(container.Resolve<IMainLogger>()));
where the module takes and exposes the logger:
public class ContainingTextBoxModule : IModule
{
public IMainLogger Logger { get; private set; }
public ContainingTextBoxModule(IMainLogger logger)
{
Logger = logger;
}
}
Then your ViewModel for the module can relay the message(s) and you can bind to it/them from your view.
Does this answer your question?

Does ICommand always requires an object as a parameter?

When I implement the ICommand interface, the following methods are created
#region ICommand Members
public bool CanExecute(object parameter)
{
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
}
#endregion
The interesting part is
public void Execute(object parameter)
{
}
Simply because it indicates that it expects 1 parameter. What if I don't need to pass a parameter? In my ViewModel I have the following code
public class DownloadViewModel : BaseViewModel
{
public ICommand BrowseForFile { get; set; }
public string File { get; set; }
public DownloadViewModel()
{
BrowseForFile = new RelayCommand(new Action<object>(OpenDialog));
}
private void OpenDialog(object o)
{
var dialog = new System.Windows.Forms.FolderBrowserDialog();
System.Windows.Forms.DialogResult result = dialog.ShowDialog();
File = dialog.SelectedPath;
}
}
The OpenDialog method does not require the parameter but it appears as if I have to just so I can satisfy the Interface.
Am I doing this right or have I missed the point?
Yes, ICommand always needs an object and RelayCommand too. If you don't need it, you pass null and don't use it in your method, which is ugly.
I would use Prism's DelegateCommand instead. This exists in a non-generic version, which doesn't take parameters:
Command = new DelegateCommand(DoSomething);
CommandWithParameter = new DelegateCommand<int>(DoSOmethingWithInt);
Its in the PRISM assembly, which you have to download and reference.
using Microsoft.Practices.Prism;
PRISM
Alternatively, use the MVVMLight toolkit, which provides a command class which does basically the same thing. There is no point in using MVVM without a MVVM framework anyway. I can recommend PRISM, also for it's basic stuff like the DelegateCommand or the EventAggregator.
The fact that Execute takes a parameter is irrelevant to the method from your ViewModel. The only thing that affects what parameters OpenDialog needs is your implementation of ICommand.
If your implementation is, for example:
public class MyRandomCommand : ICommand
{
private readonly Action _action;
public MyRandomCommand(Action action)
{
_action = action;
}
public void Execute(object parameter)
{
_action();
}
...
}
Then no parameters will be required for your OpenDialog method, as you can create a command as follows:
public ICommand Command { get { return new MyRandomCommand(OpenDialog); } }
You can, however, require any signature you like for the method you are passing to your command.
The most common, off-the-shelf implementations of RelayCommand can take methods with either 0 or 1 parameter and will be called from Execute appropriately.