Change page by button click - c#

In my xaml file i have
<Button
Command="{Binding LoginCommand}"
</Button>
<Frame
Grid.Column="1"
NavigationUIVisibility="Hidden"
Content="{Binding CurrentPage, UpdateSourceTrigger=PropertyChanged}">
</Frame>
My viewmodel class:
private readonly LoginPageView _loginPage; // Page i want to use
private readonly StartPageView _startPage;
private Page _currentPage;
public StartWindowViewModel(LoginPageView loginPage, StartPageView startPage)
{
_loginPage = loginPage;
_startPage = startPage;
CurrentPage = _startPage;
LoginCommand = new RelayCommand(OpenLoginPage, _ => true);
}
public event PropertyChangedEventHandler PropertyChanged;
public Page CurrentPage // I'm binding my frame to this property
{
get => _currentPage;
set
{
_currentPage = value;
OnPropertyChanged();
}
}
public RelayCommand LoginCommand { get; set; }
private void OpenLoginPage(object parameter)
{
CurrentPage = _loginPage;
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
I want to change page displayed in frame by clicking a button. I'm not sure if i should user Frame for this, but it seems okay. The problem that i'm not updating page,

My solution not seems to be best possible:
<Frame
Grid.Column="1"
NavigationUIVisibility="Hidden"
Source="{Binding PageName}">
</Frame>
public string PageName
{
get => _pageName + ".xaml";
set
{
_pageName = value;
OnPropertyChanged();
}
}
I don't actualy like this way of solving, so i would be happy if somebody would give a better approach.

Related

INotifyPropertyChanged Not Working, I need some guide

I made an application with Windows template studio, As MVVM,
The Problem exists in ShellPage which contains some Controls, 2 Image , TextBlock, the NavigationView, and of course the Frame that holds all other pages.
The code here is for the TextBlock, but the Problem same for the 2 Image controls also.
in ShellPage.xaml:
xmlns:myControls="using:Numbers_to_Text.MyControls"
d:DataContext="{d:DesignInstance Type=viewmodels:ShellViewModel}"
Height="650" Width="1000" MaxHeight="650" MaxWidth="1000" MinHeight="650" MinWidth="1000"
mc:Ignorable="d" Background="{x:Null}">
<Page.Resources>
<helpers:AppSettings x:Key="AppSettings" />
</Page.Resources>
<TextBlock x:FieldModifier="public" x:Name="PageTitle" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="1"
Canvas.ZIndex="2" TextAlignment="DetectFromContent" HorizontalTextAlignment="DetectFromContent"
VerticalAlignment="Bottom" FontWeight="Bold" Text="{Binding ChangeTitle, Mode=TwoWay}"/>
in ShellPage.xaml.cs:
public ShellPage()
{
InitializeComponent();
DataContext = ViewModel;
ViewModel.Initialize(shellFrame, navigationView, KeyboardAccelerators);
}
and in ShellViewModel.cs
private void OnItemInvoked(WinUI.NavigationViewItemInvokedEventArgs args)
{
if (args.IsSettingsInvoked)
{
NavigationService.Navigate(typeof(SettingsPage), null, args.RecommendedNavigationTransitionInfo);
ChangeTitle = "Settings";
}
else
{
var selectedItem = args.InvokedItemContainer as WinUI.NavigationViewItem;
var pageType = selectedItem?.GetValue(NavHelper.NavigateToProperty) as Type;
if (pageType != null)
{
NavigationService.Navigate(pageType, null, args.RecommendedNavigationTransitionInfo);
ChangeTitle= pageType.Name;
}
}
}
private string _changeTitle;
public string ChangeTitle
{
get { return _changeTitle= GetTitle(); }
set
{
_changeTitle = value;
RaisePropertyChanged(nameof(ChangeTitle));
}
}
private static string GetTitle()
{
try
{
var resourceLoader = Windows.ApplicationModel.Resources.ResourceLoader.GetForCurrentView();
return NavigationService.Frame.Content != null
? resourceLoader.GetString(NavigationService.Frame.Content.GetType().Name)
: "Error Page Title";
}
catch
{
return "Welcome to Main Page";
}
}
public event PropertyChangedEventHandler propertyChanged;
public void NotifyPropertyChanged([CallerMemberName] string propName = "")
{
propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
this.propertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Why ChangeTitle not changing when navigation occured?
I used breakPoints to trace the ChangeTitle, I implemented PropertyChangedEventHandler inside the shellViewModel instead to make sure that the property setter is call the NotifyPropertyChanged, with no luck.

How to update visibility at runtime in WPF

I am currently developing a hamburger style menu in WPF. In this menu, there are some categories that each have an icon. When the menu is collapsed you can still see those icons. When you expand the menu, there should appear text next to it. My idea was to just set their visibility to Visible as soon as the menu opens but I've had a lot of trouble realizing this. Right now I'm trying to change their visibility by binding them to a property.
XAML:
<ListView x:Name="menuItemsListView" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListViewItem>
<StackPanel Orientation="Horizontal">
<Image x:Uid="Test" Name="InhoudImage" Source="Images/noimage.png" Height="30" Width="auto" VerticalAlignment="Center" Margin="3,0,0,0"></Image>
<TextBlock x:Uid="Test" Text="{Binding Path=TextboxVisibility}" Visibility="{Binding Path=TextboxVisibility}" VerticalAlignment="Center"></TextBlock>
</StackPanel>
</ListViewItem>
</ListView>
C# CS Class:
using System.Windows;
using System.Windows.Controls;
namespace APP
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private bool menuOpen = false;
private int closedMenuWidth = 50;
private int openMenuWidth = 210;
private string textboxVisibility;
public string TextboxVisibility
{
get { return textboxVisibility; }
set { textboxVisibility = value; }
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
this.TextboxVisibility = "Hidden";
}
private void MenuButton_Click(object sender, RoutedEventArgs e)
{
if (menuOpen)
{
menuGrid.Width = closedMenuWidth;
menuOpen = false;
this.TextboxVisibility = "Hidden";
}
else
{
menuGrid.Width = openMenuWidth;
menuOpen = true;
this.TextboxVisibility = "Visible";
//foreach (ListViewItem item in menuItemsListView.Items)
//{
// item.
// if (item.Uid == "Test")
// {
// item.Visibility = Visibility.Visible;
// }
//}
}
}
}
}
When I change the value within the MainWindow function, it does have an effect on it when it first starts. But the other times I try to change it, which is at runtime, nothing happens. I have tried all sorts of things with booleans and binding the actual Visibility type but nothing worked.
You should implemente INotifyPropertyChanged on your MainWindow class like this:
public partial class MainWindow: Window,INotifyPropertyChanged {
private string textboxVisibility;
public string TextboxVisibility {
get {
return textboxVisibility;
}
set {
textboxVisibility = value;
OnPropertyChanged();
}
}
//The rest of your code goes here
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
PropertyChanged ? .Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
What OnPropertyChanged method does is, whenever the value is setted, it notifies the view and refreshes it.
This will solve the problem but isn't the right way to use MVVM.
The way you should do this is to change the visibility property of the TextBox instead of binding the visibility property to a value:
First you have to add a name to the TextBlock you want to hide:
<ListView x:Name="menuItemsListView" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListViewItem>
<StackPanel Orientation="Horizontal">
<Image x:Uid="Test" Name="InhoudImage" Source="Images/noimage.png" Height="30" Width="auto" VerticalAlignment="Center" Margin="3,0,0,0"></Image>
<TextBlock Name="textblock" x:Uid="Test" Text="{Binding Path=TextboxVisibility}" Visibility="{Binding Path=TextboxVisibility}" VerticalAlignment="Center"></TextBlock>
</StackPanel>
</ListViewItem>
</ListView>
And then you change the visibility in the code
private void MenuButton_Click(object sender, RoutedEventArgs e) {
if (menuOpen) {
menuGrid.Width = closedMenuWidth;
menuOpen = false;
textblock.Visibility = System.Windows.Visibility.Hidden;
}
else {
menuGrid.Width = openMenuWidth;
menuOpen = true;
textblock.Visibility = System.Windows.Visibility.Visible;
//foreach (ListViewItem item in menuItemsListView.Items)
//{
// item.
// if (item.Uid == "Test")
// {
// item.Visibility = Visibility.Visible;
// }
//}
}
}
If you want to implement MVVM the right way you have to create a ViewModel class and add it as Data Context to your view:
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
And then on you MainWindowViewModel is where you change the property:
public class MainWindowViewModel: INotifyPropertyChanged {
private string textboxVisibility;
public string TextboxVisibility {
get {
return textboxVisibility;
}
set {
textboxVisibility = value;
OnPropertyChanged();
}
}
//The rest of your code goes here
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) {
PropertyChanged ? .Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

Why this picker is not selecting an item as expected?

I'm trying to do a picker that loads ItemSource from a List and depending on an external event, change its SelectedIndex based on Local.id, but what I've been trying so far didn't works.
C# code:
public class Local
{
public string cidade { get; set; }
public int id { get; set; }
}
public int CidadeSelectedIndex{ get; set; }
string jsonCidades;
public async void CarregaCidades()
{
try
{
using (WebClient browser = new WebClient())
{
Uri uriCidades = new Uri("xxxxxxx.php");
jsonCidades = await browser.DownloadStringTaskAsync(uriCidades);
}
var ListaCidades = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Local>>(jsonCidades);
PickerCidades.ItemsSource = ListaCidades;
}
catch (Exception)
{
throw;
}
}
//In some moment of the execution, this code is called:
Local localizacao = JsonConvert.DeserializeObject<Local>(json);
if (localizacao.GetType().GetProperty("id") != null)
{
/*CidadeSelectedItem = localizacao;
I tried that before with SelectedItem="{Binding CidadeSelectedItem, Mode=TwoWay}" */
CidadeSelectedIndex = localizacao.id; // now trying this
}
Before I was trying to bind using ItemDisplayBinding="{Binding ListaCidades.cidade, Mode=OneWay}" but since it was not working I start to use ItemSources=ListaCidades
My XAML code:
<Picker x:Name="PickerCidades"
SelectedIndex="{Binding CidadeSelectedIndex, Mode=TwoWay}"
Grid.Column="1" Grid.Row="0"
SelectedIndexChanged="PickerCidades_SelectedIndexChanged">
</Picker>
I think it's not working because I'm setting the items using ItemsSource. I think I need to bind it using xaml. Would be nice have some help.
Do you want to achieve the result like following GIF?
My xaml layout like following code.
<StackLayout>
<!-- Place new controls here -->
<Picker x:Name="PickerCidades"
ItemsSource="{ Binding locals}"
SelectedIndex="{Binding CidadeSelectedIndex, Mode=TwoWay}"
ItemDisplayBinding="{Binding cidade}"
Grid.Column="1" Grid.Row="0"
SelectedIndexChanged="PickerCidades_SelectedIndexChanged">
</Picker>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Text="CidadeSelectedIndex: " Grid.Column="0" Grid.Row="0"/>
<Label Text="{Binding CidadeSelectedIndex}" Grid.Column="1" Grid.Row="0"/>
</Grid>
</StackLayout>
Layout background code.
public partial class MainPage : ContentPage
{
MyViewModel myViewModel;
public MainPage()
{
InitializeComponent();
myViewModel= new MyViewModel();
BindingContext = myViewModel;
}
private void PickerCidades_SelectedIndexChanged(object sender, EventArgs e)
{
var picker = (Picker)sender;
int selectedIndex = picker.SelectedIndex;
myViewModel.CidadeSelectedIndex = selectedIndex;
}
}
MyViewMode code.I use static data for testing. You can achieve the INotifyPropertyChanged interface to change dynamically.
public class MyViewModel : INotifyPropertyChanged
{
int _cidadeSelectedIndex=1;
public int CidadeSelectedIndex
{
set
{
if (_cidadeSelectedIndex != value)
{
_cidadeSelectedIndex = value;
OnPropertyChanged("CidadeSelectedIndex");
}
}
get
{
return _cidadeSelectedIndex;
}
}
public ObservableCollection<Local> locals { get; set; }
public MyViewModel()
{
locals = new ObservableCollection<Local>();
locals.Add(new Local() { cidade= "xxx0" , id= 0 });
locals.Add(new Local() { cidade = "xxx1", id = 1 });
locals.Add(new Local() { cidade = "xxx2", id = 2 });
locals.Add(new Local() { cidade = "xxx3", id = 3 });
locals.Add(new Local() { cidade = "xxx4", id = 4 });
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
If the goal is to change the User Interface from code you need to have a ViewModel that implements INotifyPropertyChanged (or inherits from a base that does). Then instead of SelectedIndex bound property being a simple get; set as below it fires off the PropertyChanged event.
public int CidadeSelectedIndex{ get; set; }
Needs to fire notification event. Something along these lines
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private int _cidadeSelectedIndex;
public int CidadeSelectedIndex
{
get => _cidadeSelectedIndex;
set {
_cidadeSelectedIndex = value;
NotifyPropertyChanged();
}
}
}

Xamarin Label IsVisible Propety isn't changing

I have a PopUp Page and in this Popup page i have a Label with IsVisible Property bound to my ViewModel. However the site is not refreshing when the IsVisible Property is changing. Whats wrong with my code?
My Xaml
<StackLayout>
<Label Text="Name der Einkaufsliste" />
<Entry x:Name="entryList" FontSize="20"
Placeholder="z.B. Lidl" />
<Label Text="Liste schon Vorhanden!" TextColor="Red" IsVisible="{Binding IsVisible, Mode=TwoWay}"/>
</StackLayout>
My xaml.cs
PopupViewModel vm = new PopupViewModel();
public PopupViewListeHinzufügen()
{
InitializeComponent();
BindingContext = vm;
}
private void Button_Clicked(object sender, EventArgs e)
{
MasterPage master = new MasterPage();
master.addList(entryList.Text);
}
public void ListeVorhandenMeldung()
{
vm.setLabelVisible();
}
My viewmodel:
public class PopupViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public PopupViewModel()
{
IsVisible = false;
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public void setLabelVisible()
{
IsVisible = true;
}
private bool isVisible;
public bool IsVisible
{
get
{
return isVisible;
}
set
{
isVisible = value;
OnPropertyChanged("IsVisible");
}
}
}
Thanks for any help!

Bind a custom view to page model in xamarin forms

I am trying to create a custom view that will be used as a header in some of the pages in the application. A custom view has a button to save info, and an image to show if the info was saved, but I can also receive info from the API if the info was saved. (this is a simplified version of the scenario)
So, I have MainPage.xaml (any page that will use the custom view)
ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Messages"
xmlns:controls="clr-namespace:Messages.Controls"
x:Class="Messages.MainPage">
<StackLayout Spacing="5">
<controls:HeaderMenu x:Name="menu" HorizontalOptions="FillAndExpand" VerticalOptions="Start" SaveCommand="{Binding MyCommand}" IsControlClosed="{Binding ControlClosedValue, Mode=TwoWay}" />
.....
</StackLayout>
MainPageViewModel.cs
public class MainPageViewModel : INotifyPropertyChanged
{
public ICommand MyCommand { get; set; }
private bool _controlClosedvalue;
public bool ControlClosedValue
{
get => _controlClosedvalue;
set
{
_controlClosedvalue = value;
OnPropertyChanged(nameof(ControlClosedValue));
}
}
public MainPageViewModel()
{
MyCommand = new Command(MyCommandExecute);
_controlClosedvalue = false;
}
private void MyCommandExecute()
{
// do stuff
_controlClosedvalue = true; //change value to change the value of control
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
HeaderMenu.xaml
<Grid>
<Image Source="save.png" HeightRequest="25" WidthRequest="25">
<Image.GestureRecognizers>
<TapGestureRecognizer NumberOfTapsRequired="1" Tapped="SaveImage_OnTapped" />
</Image.GestureRecognizers>
</Image>
<Image IsVisible="{Binding IsControlClosed}" Source="check.png" HeightRequest="30" WidthRequest="30" />
HeaderMenu.xaml.cs
public partial class HeaderMenu : ContentView
{
public HeaderMenu ()
{
InitializeComponent();
imgControlClosed.BindingContext = this;
}
public static readonly BindableProperty SaveCommandProperty =
BindableProperty.Create(nameof(SaveCommand), typeof(ICommand), typeof(HeaderMenu));
public static readonly BindableProperty IsControlClosedProperty =
BindableProperty.Create(nameof(IsControlClosed), typeof(bool), typeof(HeaderMenu), false, BindingMode.TwoWay, null, ControlClosed_OnPropertyChanged);
public ICommand SaveCommand
{
get => (ICommand) GetValue(SaveCommandProperty);
set => SetValue(SaveCommandProperty, value);
}
public bool IsControlClosed
{
get => (bool) GetValue(IsControlClosedProperty);
set => SetValue(IsControlClosedProperty, value);
}
private static void ControlClosed_OnPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
if (bindable is HeaderMenu control)
{
control.imgControlClosed.IsVisible = (bool)newValue;
}
}
private void SaveImage_OnTapped(object sender, EventArgs e)
{
if (SaveCommand != null && SaveCommand.CanExecute(null))
{
SaveCommand.Execute(null);
}
}
}
So, what I need is that when the save command is tapped to execute some code in the page that is using control, and binding of SaveCommand works as expected. But after the code is executed, or in some different cases, I wish to change the property in the page model and this should change the property on the custom view, but this does not work.
Does anyone know what is wrong with this code?
If I just put True or False when consuming control it works.
<controls:HeaderMenu x:Name="menu" HorizontalOptions="FillAndExpand" VerticalOptions="Start" SaveCommand="{Binding MyCommand}" IsControlClosed="True" />
But it does not work when binding it to the property.
I have found out what an issue was. A stupid mistake, I was setting the value of the variable instead of property.
In the main page view model, instead of
_controlClosedvalue = false; // or true
it should be
ControlClosedValue = false; // or true

Categories