ReactiveUI Example with Avalonia : UserControl View not working - c#

I tried to implement Reactive UI Example using Avalonia with ReactiveUI. The search works, I can print on the console the elements resulting from it and there is a "slot" for each of them in the UI (the lines appear but are empty), but the NuggetDetailView does not show as the list's items.
I have activated View for ViewModel scan in the Initialize method of my Avalonia app :
public class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly());
}
....
}
I do not get any error so I'm a bit lost on what I did wrong.
Thank you in advance,

Turns out the reflection-based View scanning was not working.
I changed
Locator.CurrentMutable.RegisterViewsForViewModels(Assembly.GetCallingAssembly());
to
Locator.CurrentMutable.Register(() => new NugetDetailsView(), typeof(IViewFor<NugetDetailsViewModel>));
And it worked

Related

Provide "trigger-method" to module-(sub)-view model in WPF and MVVM

I'm got an other question for my WPF/MVVM application I'm working on since a while.
The main idea is to use a main window providing a navigation bar and a ContentControl.
The different "Modules" are all built as UserControl with each its own ViewModel.
The main call from the main viewmodel to start a module is
private void ShowAddressModule() {
ContentControlBindingProperty = new AddressModule(new AddressModuleViewModel);
}
In the real application the viewmodels are pre-loaded and so on, but the start is more or less the same.
The main view model contains a boolean property LongRunningOperation to do multiple operations on the main window while any long running operation.
As example showing a loading image or disable the main navigation while loading a new module or whatever.
So my idea is to provide a possibility to the modules (their view models) to active this "mode".
Example how it could look in the modules view model:
private void LoadContactList() {
MainWindow.LongRunningOperation = true;
LoadAllContactsInAThread(); /*Takes a long time*/
MainWindow.LongRunningOperation = false;
}
I tried to mark the property as static and public, but this will not work because of the OnPropertyChanged event.
If possible it would be great if the solution could be applied also to methods (including parameters) from the main window - so (as example) the modules could use as example the parents statusbar or so.
MainWindow.ShowErrorMessageInStatusBar("The error xyz occured!");
Hopefully I described good enought, what's my idea...
And hopefully anybody could provide me the needed tip how to handle this requirement.
Thanks in advance for any hints
Regards Markus
Each module could raise an event to indicate the start of a long running operation, and raise an event to indicate the end of a long running operation. Your main view model, when loading modules for the first time, could hook-up to these events and react to them accordingly.
Your sub view model would have some events like this:
Sub view model
public delegate void OnLongRunningOperationStartedEventHandler(object sender);
public delegate void OnLongRunningOperationFinishedEventHandler(object sender);
public event OnLongRunningOperationStartedEventHandler OnLongRunningOperationStarted;
public event OnLongRunningOperationFinishedEventHandler OnLongRunningOperationFinished;
private void LoadContactList() {
OnLongRunningOperationStarted?.Invoke(this);
LoadAllContactsInAThread(); /*Takes a long time*/
OnLongRunningOperationFinished.Invoke(this);
}
And your main view model will hook-up to them like this:
Main View Model
public bool LongRunningOperation { get; private set; }
// Keep track of the number of modules currently running long operations
private int _countLongRunningOperations = 0;
public LoadSubModules(){
// Depending on how you load your sub modules, this piece of code could move around
foreach (var module in submodules){
module.OnLongRunningOperationStarted += Module_LongOperationStarted;
module.OnLongRunningOperationFinished += Module_LongOperationFinished;
}
}
private void Module_LongOperationStarted(object sender){
_countLongRunningOperations += 1;
LongRunningOperation = true;
}
private void Module_LongOperationFinished(object sender){
_countLongRunningOperations -= 1;
if (_countLongRunningOperations == 0) {
LongRunningOperation = false;
}
The same principle (using events) could be used to bubble up error messages from each submodule to the main view model.
The quick and very dirty approach:
Grab a reference to mainwindow out of application.current.mainwindow. Cast it to MainWindow. It's a property set to whatever the first window you show is - MainWindow just happens to be the default name of the main window.
You can then set the property on that if it's a public dependency property. Make sure the dp binds twoway in it's metadata.
This is bad because you're referencing ui in your viewmodels and you have no application when you run tests on viewmodels in some test runner.
The quick and dirty approach
Add a public property to app and set this to your instance of mainwindowviewmodel in it's ctor. You can reference app from an piece of code. Add a public property to mainwindowviewmodel and bind to that.
This is bad because you have no application when you run tests on viewmodels in some test runner.
You could add a static with an interface abstracts this away and work round that though.
My suggestion
This hinges on the fact you can use dot notation to bind and that includes
Content.IsBusy on yourcontentcontrol.
You can therefore bind from a parent window to a dependency property of any usercontrol that happens to be in it's contentcontrol.
Add that property using an attached property and bind that to IsBusy in a base viewmodel. Inherit the viewmodels of your child views from that.
One thing to mention is that binding to an attached property is a little odd and rather than just
ElementName=YourContentControl, Path=Content.YourAttachedProperty
You need something like:
ElementName=YourContentControl, Path=Content.(local:AttachClass.YourAttachedProperty)

Xamarin XAML ListView - How to select programmatically

I am developing a windows mobile application with Xamarin, but don't seem to be able to programmatically set the selected ListView item.
I have tried the following ListViews methods and still nothing
SelectedItem
ScrollTo()
Focus()
I have also googled it and can't seem anything to say how to do this.
How do I do this?
This works fine in my sample app:
public partial class ItemsPage : ContentPage
{
public ItemsPage()
{
InitializeComponent();
Vm = new ItemsViewModel();
BindingContext = Vm;
}
protected override void OnAppearing()
{
ListviewItems.SelectedItem = Vm.Items[1];
}
public ItemsViewModel Vm { get; private set; }
In my sample app, ItemsViewModel.Items is a List<string>.
The second item in the list is set selected after this line of code runs in OnAppearing.
setting the SelectedItem property is the "correct" way to do it. What specifically is not happening that you think you happen when it is set?
I used a method but it may not be the most effective one. You can set your change to the list which was created by your model type and then you should reload the list view.
subjects[i].something=false;//set something
yourListView.ItemSource=null;
yourListView.ItemSource=subjects;
if you want to scroll to the specific location,
I am using
listChat.SetSelection(currentIndex);
in one of my chat application and it works fine.
if you are looking for the solution to scroll to desired position, even I have searched for the solution on internet, nothing helped. finally this one was my work around to make it work.

Correct way to transfer SelectedItemViewModel to another page in Windows Phone 8.1

I'm currently building a universal app but I'm concentrating on the WP8.1 part of it right now. I'm using MVVMLight with this project.
For simplicity sake, we'll just assume that I only have 2 pages in the project.
Page1.xaml contains a list which has various items. The Page1.xaml is binded to its own ViewModel i.e. Page1ViewModel. Each item in the list represents a viewModel i.e. ItemViewModel.
When I tap on an item, I call the following code:
public RelayCommand<ItemViewModel> ItemTapCommand
{
get
{
return this._itemTapCommand ?? (this._itemTapCommand =
new RelayCommand<ItemViewModel>((msg) =>
ExecuteItempTapCommand(msg)));
}
}
When an item in the list is tapped, I call the following code:
private object ExecuteItempTapCommand(ItemViewModel selectedItemViewModel)
{
Page2ViewModel page2ViewModel =
SimpleIoc.Default.GetInstance<ItemViewModel>();
page2ViewModel.SelectedItem = selectedItemViewModel;
_navigationService.Navigate(typeof(Page2),
selectedItemViewModel);
return null;
}
As you can see I'm using my Ioc to create get an instance of my Page2ViewModel and I then set the SelectedItem to the selectedItemViewModel.
Once it is set, I navigate to Page2 which is binded to my Page2ViewModel.
What I want to know is, is the above is ok to do? I've seen plenty of examples when dealing with passing object from one page to another is done by passing an Id for example and then I request the information from Page2, but why request it again when most of the information I need is already in my SelectedItemViewModel since it represents the tapped item in my list in Page1.
If it's not correct, what is the best way to go about this using MVVMLight?
Can you provide a sample? I've seen something about Messaging but I'm not sure how this would work as if I navigate to my page2, the Page2ViewModel will only be initiated when the page is created, so how can it receive a message? The way I have it above seems to initiate the Page2ViewModel and my Pag2 loads, it's re-using it and everything bind correctly but I'm not sure this is the correct way to go about it.
Any help would be appreciated.
Thanks.
In your Page2ViewModel, why not use
protected override void OnNavigatedTo(NavigationEventArgs e)
{
Page2SelectedItem = e.Parameter as ItemViewModel;
base.OnNavigatedTo(e);
}
It looks like you are packing that data in with your _navigationService.Navigate call already.
With that set up, what happens if you just change to:
private object ExecuteItempTapCommand(ItemViewModel selectedItemViewModel)
{
_navigationService.Navigate(typeof(Page2), selectedItemViewModel);
return null;
}
You can use the ViewModel to get it if you do some work before that.
Read this blog post by Marco Minerva called Calling ViewModel methods in response to Page navigation events using MVVM Light in WinRT
which explains how to react to OnNavigatedTo and OnNavigatedFrom in the ViewModel.
It's a very cool solution.

DataWindowButton CanExecute not firing, Catel 4.0

I have just updated a project from Catel 3.4 to Catel 4.0 and a custom apply button that had been working now never gets enabled.
AddCustomButton(new DataWindowButton("Apply", ExecuteApply, canExecuteApply));
In Catel 3.4 the canExecuteApply got called when the window got focus or any control was changed. In 4.0 it gets called twice when the window is created and never again.
I suspect this has something to do with the IViewPropertySelector part of the update guide, however registering the default implementation had no effect and I can't figure out what namespace the AutoDetectViewPropertiesToSubscribe extension method is in.
Edit: I have found I am getting the same behavior with some AsynchronousCommand instances elsewhere in the application. The CanExecute delegate fires when the control is created then never again.
Edit 2: These were the same issue with diffrent solutions. For an explanation of the issue see Geert van Horrik's answer.
If the command is registered in a view model you can use
ViewModelCommandManager.InvalidateCommands(true);
to get the can execute state to re-evaluate. For a DataWindowButton as described above I had to manually call RaiseCanExecuteChanged on the button's command since that command does not belong to a vie model as far as i can tell.
var catelCommand = (applyButton.Command as ICatelCommand);
if (catelCommand != null)
{
catelCommand.RaiseCanExecuteChanged();
}
In either case, this is far from the approach with the best performance characteristics, but if the same behavior you had before the upgrade is desired, you can make these calls in the following event subscription:
System.Windows.Input.CommandManager.RequerySuggested += RequerySuggested;
Hope this helps anyone else facing this issue.
The reason is that in the past (pre 4.0), Catel subscribed to the CommandManager of WPF and invalidated all the commands on all view models on nearly everything (mouse move, focus, etc). To improve performance (a lot), we decided to only invalidate commands automatically when a property changes on a specific view model.
For example, if you have a vm where you change a property, it will automatically re-evaluate the commands on that vm. You can still manually re-evaluate commands using this code (inside a vm):
ViewModelCommandManager.InvalidateCommands(true);
How about this? I had a problem where my nested user controls must cause my outer user control's commands to update. It is not elegant but it gets the job done for me, until I can get a better way.
public partial class App : Application
{
private static IViewModelManager _ViewModelManager;
public App()
: base()
{
var dependencyResolver = this.GetDependencyResolver();
_ViewModelManager = dependencyResolver.Resolve<IViewModelManager>();
System.Windows.Input.CommandManager.RequerySuggested += RequerySuggested;
}
private void RequerySuggested(object sender, EventArgs e)
{
foreach (IViewModel viewModel in _ViewModelManager.ActiveViewModels)
{
(viewModel as ViewModelBase).GetViewModelCommandManager().InvalidateCommands(true);
}
}
}

Issue with opening Child window from Main window using WPF with MVVM

I am learning WPF with M-V-VM. And I am using ICommand, RelayCommand.
I have several Views, Models, and ViewModels.
The MainWIndowView open upon on application start. The MainWindowView has a button that opens another WPF window called “FileListview” via MainWindowViewModel.
The FileListView has a button “View Lookup”, which supposed to open another WPF window called “LookupView” via FileListViewModel. But I could not make this button to work unless I specify FileListView in App.xaml.cs instead of MainWIndowView. I could not understand why “View Lookup” button work if I make application to start from “FileListView” . I also don’t understand whether I need model for MainWindowView, and FileListView since I don’t have anything going except one view’s button is opening another view.
On code behind file “App.xaml.cs” I have
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
WPFProject. MainWIndowView window = new MainWIndowView ();
MainWIndowViewModel VM = new MainWIndowViewModel ();
window.DataContext = VM;
window.Show();
}
}
I would appreciate if somebody can point me to good article or sample code using WPF with M-V-VM that reflect my issue.
Here is my approach to use dialogs/child windows with mvvm and wpf. please note the comment from sllev and post all relevant code.
After rethinking the issue, I was able to figure out the solution.
The cause of the issue: I was not associating View with it’s ViewModel class.
So I put the following code in code behind of FileListView.xaml.cs.
public partial class FileListView: Window
{
private FileListViewModel _ fileListViewModel = new FileListViewModel ();
public FileListViewModel ()
{
InitializeComponent();
base.DataContext = _fileListViewModel;
}
}
Thank you

Categories