WPF problem MainWindow Frame not changing Source property from ViewModel - c#

I have problem to display another page inside MainWindow frame. When program starts it shows HomePage.xaml properly inside frame. It takes PagePath property from VM and binding works.When I try display another Page1.xaml in MainWindowViewModel, PagePath is changing, but PropertyChange event is always null and not invoking new property to frame source.
Code looks like this:
MainWindow.xaml
<Window x:Class="TEST.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TEST.ViewModels"
mc:Ignorable="d"
Title="TEST" Height="768" Width="1024"
WindowState="Maximized">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Frame Source="{Binding PagePath}" NavigationUIVisibility="Hidden" />
</Grid>
</Window>
MainWindowViewModel.cs
namespace TEST.ViewModels
{
public class MainWindowViewModel : INotifyPropertyChanged
{
private static MainWindowViewModel _instance = new MainWindowViewModel();
public static MainWindowViewModel Instance { get { return _instance; } }
public event PropertyChangedEventHandler PropertyChanged;
private string pagePath = "/Pages/HomePage.xaml";
public string PagePath
{
get { return pagePath; }
set
{
pagePath = value;
OnPropertyChanged(nameof(PagePath));
}
}
protected void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
HomePage.xaml
<Page x:Class="TEST.Pages.HomePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:TEST.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="Home Page">
<Page.DataContext>
<local:HomePageViewModel />
</Page.DataContext>
<Grid>
<StackPanel>
<Button
Command="{Binding ShowPage1Command}"
Content="Show Page1"/>
</StackPanel>
</Grid>
</Page>
HomePageViewModel.cs
namespace TEST.ViewModels
{
public class HomePageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public ICommand ShowPage1Command { get; private set; }
public HomePageViewModel()
{
ShowPage1Command = new DelegateCommand((e) => ShowPage1());
}
protected void OnPropertyChanged(string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void ShowPage1()
{
MainWindowViewModel.Instance.PagePath = "/Pages/Page1.xaml";
}
}
}
How to solve the problem or maybe it is wrong approach to navigate with frame?

Related

WPF Command Not Executing

My main window is currently displaying the YearView in a content control via DataTemplated YearViewModel. When I click the month button on the YearView I want the main window to instead display the MonthView. The MainViewModel (view model of the main window not shown) retrieves the view model to be displayed from '_navigationStorage.CurrentViewModel' as seen in NavigateMonthCommand.Execute(). But when I click the month button, NavigateMonthCommand.Execute() is never called.
Is the month button binding not working? In YearView.xaml should I be specifying the DataContext differently?
MainWindow.xaml
<Window x:Class="Calandar.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodels="clr-namespace:Calandar.ViewModels"
xmlns:views="clr-namespace:Calandar.Views"
xmlns:local="clr-namespace:Calandar"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Background="LightSlateGray">
<ContentControl Content="{Binding CurrentViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type viewmodels:YearViewModel}">
<views:YearView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewmodels:MonthViewModel}">
<views:MonthView/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</Grid>
</Window>
YearView.xaml
<UserControl x:Class="Calandar.Views.YearView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Calandar.Views"
xmlns:viewmodels="clr-namespace:Calandar.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="White" DataContext="viewmodels:YearViewModel">
<StackPanel>
<TextBlock Text="Year" FontSize="55"/>
<Button Content="Month" Command="{Binding NavigateMonthCommand}"/>
</StackPanel>
</Grid>
</UserControl>
YearViewModel.cs
public class YearViewModel : ViewModelBase
{
public ICommand NavigateMonthCommand { get; }
public YearViewModel(NavigationStorage navigationStorage)
{
NavigateMonthCommand = new NavigateMonthCommand(navigationStorage);
}
}
NavigateMonthCommand.cs
public class NavigateMonthCommand : CommandBase
{
private readonly NavigationStorage _navigationStorage;
public NavigateMonthCommand(NavigationStorage navigationStorage)
{
_navigationStorage = navigationStorage;
}
public override void Execute(object parameter)
{
;
_navigationStorage.CurrentViewModel = new MonthViewModel();
}
}
CommandBase.cs
public abstract class CommandBase : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter) => true;
public abstract void Execute(object parameter);
public void OnCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, new EventArgs());
}
}
Look this one
Without iNotifyPropertyChanged Viewmodel cannot get data from view interface. You must connect properly. You should derive ViewModelBase from INotifyPropertyChanged.
I think you have created your BaseViewModel, you can create the method there.
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
you need to call it while doing property set operation in the YearViewModel you specified. Sample:
private string surName;
public string SurName
{
get { return surName; }
set { surName = value; OnPropertyChanged(nameof(SurName)); }
}
Good Luck

XAML: Bind View to ViewModel from different namespace

I'm sure this is a basic question, but I can't figure out how to do this. I have a View in one namespace, and ViewModel in another namespace. How do I bind these two together?
My View is in namespace of MyProject.View. My ViewModel is in namespace of MyProject.ViewModel.
How do I bind this in XAML? I'm doing this in UWP, but I assume this is the same regardless of UWP/WPF/X.Forms.
<Page
x:Class="MyProject.View.MainPage"
xmlns:vm="using:MyProject.ViewModel"
DataContext="{Binding MainPageViewModel, Source={StaticResource vm:MainPageViewModel}}"
According to your description, I've made a code sample for your reference:
public class MainViewModel:INotifyPropertyChanged
{
private string _Name;
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get { return _Name; }
set
{
_Name = value;
RaisePropertyChanged("Name");
}
}
public MainViewModel()
{
this.Name = "Hello UWP!";
}
private void RaisePropertyChanged(string PropertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,new PropertyChangedEventArgs(PropertyName));
}
}
}
<Page
x:Class="AppViewModel.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:AppViewModel"
xmlns:vm="using:AppViewModel.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.DataContext>
<vm:MainViewModel></vm:MainViewModel>
</Page.DataContext>
<Grid>
<TextBlock Text="{Binding Name}"></TextBlock>
</Grid>
I presume you want to do something like that:
<Page x:Class="MyProject.View.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:VM="clr-namespace:MyProject.ViewModels">
<Page.BindingContext>
<VM:MainPageViewModel />
</Page.BindingContext>
<!-- content here -->
</Page>
This will create a new instance of your MainPageViewModel each time you create a new MainPage.

Binding not updating WPF

I am trying bind an int to a label in a simple application. The idea is, when a button is pressed, the int, and hence the label, is updated.
I have simplified the code as much as I can but I am not able to see the problem.
I think the issue is with NotifyPropertyChanged(String propertyName) as at runtime start the label's content is updated with the value of the int. However, when the int updates, the label does not.
MainWindow.xaml
<Window x:Class="Press.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="Presses: "/>
<Label Content="{Binding Path=PressCount}"/>
</StackPanel>
<Button Content="Press Me" Click="PressMe_Click"/>
</StackPanel>
</Window>
MainWindow.xaml.cs
using System;
using System.Windows;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace Press
{
public partial class MainWindow : Window
{
public int pressCount = 0;
public int PressCount {
get {
return pressCount;
}
private set {
if(value != pressCount)
{
pressCount = value;
NotifyPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void PressMe_Click(object sender, RoutedEventArgs e)
{
PressCount++;
Console.WriteLine(PressCount);
}
}
}
You need MainWindow to implement the interface INotifyPropertyChanged

Call frame control from viewmodel on Windows universal app

my code of MainPage.xaml:
<Page
x:Class="MyProject.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MyProject"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:x1="using:System"
mc:Ignorable="d">
<Frame HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" Name="frameMainPage"></Frame>
My code from LoginViewModel.cs
...
if (m_login.Username == "username" && m_login.Password == "password")
{
Singleton.User = m_login;
App.log.Info("Success! Login done!");
//load in frame the dashboard page after login.
}
...
How to get my frame and load in Dashboard.xaml page ?
It's possible do this from viewmodel ?
My target is load a pages in the MainPage.xaml frame.
Thanks
I want call frameMainPage from viewmodel and load new page inside it frame.
For this scenario, you could use Frame SourcePageType property to realize. If you want to the Frame loads a new page, you could modify SourcePageType's binding item like the follow.
internal class MainPageViewModel : INotifyPropertyChanged
{
private Type _nextPage;
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public Type NextPage
{
get
{
return _nextPage;
}
set
{
_nextPage = value;
OnPropertyChanged();
}
}
public MainPageViewModel()
{
_nextPage = typeof(HomePage);
}
}
MainPage.xaml
<Page.DataContext>
<local:MainPageViewModel x:Name="ViewModel" />
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Frame SourcePageType="{Binding NextPage,Mode=TwoWay}">
</Frame>
</Grid>

wpf textblock binding not working

I am new to WPF programming, so forgive me if this is a simple issue.
I have my Mainwindow.xaml set up like so:
<Window x:Class="GNMAwpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:GNMAwpf"
Title="Uploader"
Height="353" Width="342" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.DataContext>
<local:GinniNet x:Name="g" />
</Window.DataContext>
Further down I have a textblock:
<TextBlock Text="{Binding Path=StatusText}" Grid.ColumnSpan="2" MinWidth="150"></TextBlock>
In my class i have :
class GinniNet : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _statusMessage;
public string StatusMessage
{
get
{
return _statusMessage;
}
set
{
_statusMessage = value;
OnPropertyChanged("StatusMessage");
}
}
public GinniNet()
{
StatusMessage = "Ready";
}
private void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
When I change StatusMessage The textblock does not update ever. Where am i making my mistake?
Binding is incorrect -
<TextBlock Text="{Binding Path=StatusText}"/>
It should be -
<TextBlock Text="{Binding Path=StatusMessage}"

Categories

Resources