Xamarin Forms ListView Scroll position - MVVM - c#

I'm searching about this article for weeks!
UPDATE
Is controlling Listview scroll position in xamarin forms with MVVM pattern possible or not? getting and setting the scroll position of listview.
There are some answers like implementing event aggregation or using messaging or ... .
Neither of them weren't operating for me. The messaging is not fully MVVM pattern way. The event aggregation didn't work even I'm using prism 7.
There is no such a good example code or any nuget package.
Has anyone encountered this problem and solved it?

CustomersViewModel
public ObservableCollection<Customer> Customers {get;set;}
public Func<Customer,Task> ScrollToCustomer {get;set;}
public DelegateCommand FindBestCustomer {get;set;} // For example
FindBestCustomer = new DelegateCommand(async() =>
{
Customer bestCustomer = Customers.Last(); // this is our logic in view model!
await ScrollToCustomer(bestCustomer);
// at this time, scroll is finished.
});
CustomersView.xaml
<StackLayout>
<ListView x:Name="CustomersListView" ItemsSource={Binding Customers} />
<Button Text="Go to last customer" Command="{Binding ScrollToLastCustomer}" /> // For example
</StackLayout>
CustomersView.xaml.cs
override OnBindingContextChanged()
{
if(BindingContext is CustomersViewModel customersViewModel)
{
customersViewModel.ScrollToCustomer = customer => CustomersListView.ScrollTo(customer);
}
}

Related

How to navigate between page and open window using MVVM in WPF?

I have read several article, tutorial, example. But I'm still unable to make navigation between page and windows happens.
> Visual Studio Community 2019
> .NET Framework: 4.7.2
To make it simple, I have 3 pages and 1 window ALL VIEW IS FULL SCREEN FOR KIOSK APPLICATION. Details page as below:
MVVMApps.sln
│
├── PageInitialize.xaml
├── PageHome.xaml
└──── PageSelectLanguage.xaml
└── WinMessage.xaml
I have try MvvmLight by GalaSoft but stuck on navigate from PageInitialize.xaml to PageHome.xaml. And I just found article from GalaSoft that WPF is coming soon for INavigationService. Most tutorial I found is sampling for Xamarin.
Plus I got undefined assembly using Microsoft.Practices.ServiceLocation; which I see it's available on Enterprise. On Nuget, installing MvvmLight will install CommonServiceLocator too.
public class ViewModelLocator
{
private static bool initialized;
public ViewModelLocator()
{
//Fix to keep blend happy
if (initialized) { return; }
initialized = true;
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<PageInitializeViewModel>();
SimpleIoc.Default.Register<PageHomeViewModel>();
SimpleIoc.Default.Register<PageSelectLanguageViewModel>();
SimpleIoc.Default.Register<WinMessageViewModel>();
SetupNavigation();
}
public MainViewModel Main => ServiceLocator.Current.GetInstance<MainViewModel>();
public PageInitializeViewModel PageInitializeViewModel => ServiceLocator.Current.GetInstance<PageInitializeViewModel>();
public PageHomeViewModel PageHomeViewModel => ServiceLocator.Current.GetInstance<PageHomeViewModel>();
public WinMessageViewModel WinMessageViewModel=> ServiceLocator.Current.GetInstance<WinMessageViewModel>();
public static void Cleanup()
{
// TODO Clear the ViewModels
}
private void SetupNavigation()
{
var navigationService = new Helpers.NavigationService<Helpers.NavigationPage>();
navigationService.ConfigurePages();
SimpleIoc.Default.Register<Helpers.INavigationService<Helpers.NavigationPage>>(() => navigationService);
}
}
I have go through some of tutorial without MvvmLight:-
Navigating between views in WPF / MVVM
Navigation with MVVM
MVVMTest
Each article use different approach and since I'm not familiar with it, once error line appear and no Intellisense suggestion, I cannot continue to find the solutions.
Is it hard to use MVVM in WPF if I have multiple Page and Window screen? Currently, I have a complete WPF App but it use code-behind. I want to move to MVVM since I've read that MVVM is better than code-behind somewhere. MVVM for single page is not a problem as I have done before and it is totally awesome when using MVVM.
Should I retain to use code-behind in WPF if navigation is almost impossible to have workable answer?
Start simple and set navigationservice, viewmodellocators and everything but the basics to one side for now.
I would avoid all those ....locator classes anyhow. They necessarily rely on an anti pattern IMO.
Just use viewmodel first and a single window app.
The basic pattern involves a MainWindow, the Datacontext of which is MainWindowViewModel.
You might want a menu or some such but the part you will switch out is the content of a contentcontrol.
Bind the Content property of your ContentControl to a public object property on mainwindowviewmodel. Call that CurrentViewModel for the sake of discussion.
Define a viewmodel and usercontrol per view you will switch between. Thus HomeView and HomeViewModel, LoginView and LoginViewModel. And so on.
In a resource dictionary, create a datatemplate for each view associating your usercontrol with the type of it's viewmodel.
Merge this resource dictionary in app.xaml.
To navigate.
Instantiate a new viewmodel of the sort you need.
Set CurrentViewModel to that instance.
It will then be templated into UI.
There are numerous variations of this - it's called viewmodel first and you should be able to easily google a few examples.
Here's one I wrote for a slightly different purpose:
https://social.technet.microsoft.com/wiki/contents/articles/52485.wpf-tips-and-tricks-using-contentcontrol-instead-of-frame-and-page-for-navigation.aspx
You can use a similar approach with pages if you really want pages:
A simplified view:
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate DataType="{x:Type local:Page1ViewModel}">
<local:Page1/>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Page2ViewModel}">
<local:Page2/>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListBox Name="ViewSelect"
ItemsSource="{Binding ViewChoices}"
SelectedItem="{Binding SelectedViewChoice, Mode=TwoWay}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Frame Grid.Column="1"
Content="{Binding SelectedViewChoice.VM}"
NavigationUIVisibility="Hidden"
/>
</Grid>
</Window>
The very much simplified viewmodel.
public class MainWindowViewModel : INotifyPropertyChanged
{
public ObservableCollection<ViewChoice> ViewChoices { get; set; }
= new ObservableCollection<ViewChoice>
{
new ViewChoice{ Name="Page One", VM=new Page1ViewModel()},
new ViewChoice{ Name="Page Two", VM=new Page2ViewModel()},
};
private ViewChoice selectedViewChoice;
public ViewChoice SelectedViewChoice
{
get { return selectedViewChoice; }
set { selectedViewChoice = value; RaisePropertyChanged(); }
}
ps
If you decide to learn prism I would start with delegatecommand and stop there until you have written at least one wpf app.
There's a HUGE slew of functionality in PRISM and most apps don't actually benefit from regions and dynamic composition.
If you prefer mvvmlight ( I do ) then for core you're best getting the source code and using that. You want commandWPF namespace and this has a reliance on net old in the nuget package. The version that does not will not support command canexecute requery well.
I hope that's sufficient info without being overwhelming. Tricky to both be clear and not drown someone with info.

Property binding to a Child UserControl

I've got a problem I couldn't get solved until now:
I am developing an application in C#/WPF and am using the Caliburn.micro as framework. I have multiple menu panels (as user controls) that I want to reuse all over the application (e.g. data filtering menu for a grid) and show in a <ContentControl />. Depending on the state of the application a different menu panel can be shown.
Now I could get managed to let events bubble up from the menu's View to the parent's ViewModel. But I'm stuck with properties:
For example in the filtering menu, one should enter a text while the filter is instantly applied. When I had the menu in the parent's View it was easy: I just made the filtering in the property's setter method.
Is there a possibility to make a kind of "property-bubbling" similar to the message bubbling in c.m (it has to be twoWay!)? Or any other (better) MVVM-compliant approach?
Thanks in advance!
Jan
Minimal example:
ParentView.xaml
<UserControl x:Class="App.ParentView">
<Grid>
<ContentControl x:Name="Toolbar" />
</Grid>
</UserControl>
ParentViewModel.cs
class ParentViewModel : Screen
{
public ParentViewModel()
{
Toolbar = new MenuViewModel();
}
private Screen _toolbar;
public Screen Toolbar
{
// get, set ...
}
public void LoadDifferentMenu()
{
this.Toolbar = new DifferentMenuViewModel();
}
}
MenuView.xaml
<UserControl x:Class="App.MenuView">
<Grid>
<TextBox x:Name="MyText" />
</Grid>
</UserControl>
MenuViewModel.cs
class MenuViewModel : Screen
{
public MenuViewModel()
{
}
private string _myText;
public string MyText
{
// get, set...
}
}
Use Event Aggregator in caliburn micro to implement publisher and subscriber pattern in MVVM.
Communication is based on message type so it can be used for one way or two way communication with appropriate types.
Kindly refer to the link https://caliburnmicro.com/documentation/event-aggregator for implementation details.

TabControl looses child controls

I am currently working on a just for fun solution for myself, to create workout plans for the gym.
Solution on Github
It is a simple WPF solution using Caliburn.Micro to easier get the connections for the MVVM pattern.
On my MainView I have a TabControl:
MainView.xaml:
<TabControl x:Name="Items" />
With the following code for the ViewModel:
public MainViewModel()
{
DisplayName = "Plan2Fit";
Items.Add(new CreatePlanViewModel(_exerciseProviderViewModel));
Items.Add(new ExerciseManagementViewModel(_exerciseProviderViewModel));
}
I only have two ViewModels displayed in the TabControl, one to manage exercises and store them in xml to have kind of a database of your exercises and one where you should later be able to pick exercises for your plan.
Problem:
At startup everything looks ok, but as soon as I switch between the Tabs, one of them might loose its child control for whatever reason.
I have already tried the following:
MainView.xaml:
<TabControl x:Name="Items" cal:Message.Attach="[Event SelectionChanged] = [Action Reload]" />
MainViewModel:
public void Reload()
{
_exerciseProviderViewModel = new ExerciseProviderViewModel();
Items.Refresh();
DisplayName = "Plan2Fit";
}
This makes the error happen less often but it still is existing.
I have already found this question ... the solutions I was able to find are all working with MVVM, but not with Caliburn.Micro, so I am really not havinmg any Idea, how to solve this.
I have tried Avalon dock, but I was not able to get it to work with the Caliburn way of binding x:Name="Items"
Note:
If you want to recreate the Bug using my solution, you have to add some "Exercises" by picking an image in the "Manage Exercise" tab and click Add (You can add the same "Exercise" multiple times).
There is no errorhandling or testing done so far, as this is at the state where I want to validate, if it works at all.
I found the problem, still I dont understand, why it is a problem at all.
Given:
public MainViewModel()
{
DisplayName = "Plan2Fit";
Items.Add(new CreatePlanViewModel(_exerciseProviderViewModel));
Items.Add(new ExerciseManagementViewModel(_exerciseProviderViewModel));
}
The tabcontrol will occasionally loose its childs.
If I pass a new ExerciseProviderViewModel into eacht Items.Add() call, the bug does not occur.
Thus I stored a ExerciseProvider as member and passed this one into my ViewModels I want to add.
public MainViewModel()
{
_exerciseProvider = new ExerciseProvider(new DCSerializer<List<Exercise>>());
DisplayName = "Plan2Fit";
ActivateItem(new CreatePlanViewModel(new ExerciseProviderViewModel(_exerciseProvider)));
ActivateItem(new ExerciseManagementViewModel(new ExerciseProviderViewModel(_exerciseProvider)));
}
This works without any problems.

Binding button to function

How to bind button to function in "MVVM style".
I just started to convert my app to mvvm style.
my code:
XAML in Page1.xaml class:
<Button x:Name="my_button" Content="Add" Command="{Binding msgbox}" Margin="451,82,39,0" Width="50" Height="31"/>
ViewModelPage1.cs class:
public class ViewModelPage6
{
public void msgbox()
{
MessageBox.Show("mvvm is great");
}
}
Can someone give a simpler answer then here
Thanks,
Flufy.
Here's a simple example which binds a button:
https://social.technet.microsoft.com/wiki/contents/articles/32164.wpf-mvvm-step-by-step-2.aspx
Using icommand directly is a bit of a nuisance for most purposes, so you want to look into a framework which makes that easier. The sample uses mvvmlight, other frameworks are available but this is a good one. Especially for a beginner.
You cannot bind a method to the Command property of a Button directly. You will have to use an implementation of ICommand.
This should get you started..
public ICommand msgboxCommand = new DelegateCommand(msgbox);
private void msgbox()
{
...
}
See this for more details: https://www.wpftutorial.net/DelegateCommand.html
Besides, using a UI component like Messagebox in the ViewModel directly; isn't exactly MVVMish

Simple DataBinding

I want to get into DataBinding and currently I'm stuck. I just can't get it to work. I read many tutorials, but honestly, none of the really helped me. I know what DataBinding is and why it's cool to use it, but I never came across a tutorial that showed me what to do in my code. They all just assume I know what I have to do there and only show the XAML side.
This is my class:
public class Test : Window
{
public IList<String> data { get; set; }
public Test() {
data = new List<String>();
InitializeComponents();
data.Add("Hello");
data.Add("World");
}
}
And here's my XAML
<ListBox HorizontalAlignment="Left" Margin="6,6,0,6"
Name="SourceDocumentsList" Width="202"
ItemsSource="{Binding Source={x:Static Application.Current}, Path=data}" />
Yet, nothing is displayed when I render the window. How can something this easy fail? What am I doing wrong here?
The way I understand it, I tell the Listbox that it should bind itself to the data property of the currently running application, which is my class Test.
The currently running application is not that class, it's just a window, what you bind to is the instance of the App class. You cannot statically get that window instance this way. How the binding should be made depends on where that XAML is (if it is in the Test window you can for example use RelativeSource={RelativeSource AncestorType=Window} instead).
I would recommend reading the MSDN documentation on data binding and this article on debugging.
Move those properties into a separate class like
public class ViewModel
{
public IList<String> Data { get; set; }
public ViewModel()
{
Data = new ObservableCollection<string>();
Data.Add("Hello");
Data.Add("World");
}
}
Change your Window Code Behind as
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
Your Xaml will look less complicated
<ListBox HorizontalAlignment="Left" Margin="6,6,0,6"
Name="SourceDocumentsList" Width="202"
ItemsSource="{Binding Data}" />
This is what we call moving into MVVM pattern. Happy Coding !

Categories

Resources