In my previous question I believe I wasn't clear so I didn't get any answer that can help, so maybe I will post some code examples.
GreenScreen.xaml
<UserControl x:Class="WPFTut.GreenScreenView"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="Green">
<Label Content="{Binding ScreenViewModel.ScreenName}"/>
<Label Content="{Binding ScreenViewModel.UniqueProperty}"/>
</Grid>
RedScreen.xaml
<UserControl x:Class="WPFTut.RedScreenView"
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"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid Background="Red">
<Label Content="{Binding ScreenViewModel.ScreenName}"/>
</Grid>
Main.xaml
<Window x:Class="WPFTut.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="LayoutRoot">
</Grid>
IScreenViewModel.cs
namespace WPFTut
{
interface IScreenViewModel
{
string ScreenName { get; set; }
}
}
RedScreenViewModel.cs
namespace WPFTut
{
class RedScreenViewModel:IScreenViewModel
{
public string ScreenName
{
get; set;
}
}
}
GreenScreenViewModel.cs
namespace WPFTut
{
class GreenScreenViewModel : IScreenViewModel
{
public string ScreenName { get; set; }
public string UniqueProperty { get; set; }
}
}
ScreenViewModelWrapper.cs
using System.ComponentModel;
namespace WPFTut
{
class ScreenViewModelWrappers : INotifyPropertyChanged
{
private IScreenViewModel screenViewModel;
private IScreenViewModel ScreenViewModel
{
get { return screenViewModel; }
set
{
if (screenViewModel != value)
{
screenViewModel = value;
OnPropertyChanged("ScreenViewModel");
}
}
}
public void SwapViewModel(MainWindow mainWindow)
{
//Yeah it was done statically, but in my original code, it is actually dynamic to switch between
//any screens. Just to keep it simlpe
mainWindow.LayoutRoot.Children.Clear();
mainWindow.LayoutRoot.Children.Add(new GreenScreenView()); // here add randomly chosen screen from array of screens.
mainWindow.UpdateLayout(); //it doesn't refresh the visual tree. So if a red screen was selected next, an exception is thrown
ScreenViewModel = new GreenScreenViewModel(); //
}
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
var s = new PropertyChangedEventArgs(propertyName);
if (handler != null)
{
handler(this, s);
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
So that's the quick example I could write up.
Again, in the ViewModelWrapper the screen is selected dynamically based on user input.
Now assuming GreenScreenView was chosen first, when you delete and clear to add RedScreen
and you change the ScreenViewModel to RedScreenViewModel when it tries to update the bindings, it still sees the GreenScreenView!
Hence it tries to look for this:
<Label Content="{Binding ScreenViewModel.UniqueProperty}"/>
But that code doesn't belong to RedScreenView, so how do I update the visual tree to look at the correct xaml tree? Because it is throwing a NullReferenceException.
I really don't know if I can make it any clearer.
Thanks again.
Link to previous question:
Refresh View databindings using UpdateLayout or some other alternative
Related
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
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?
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.
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>
I have been a few hours now trying to understand how to do data-binding.
Initially I was following some examples but they all show to do the databinding using {Binding Source={StaticResource myObject}, Path=myObject.myProperty}
or {Binding Path=myObject.myProperty}
Nothing of this seem to bind the Config object inside the controller that is inside the Window.
If I do the binding as an StaticResource it does the binding to an object of the Controller class but is NOT the object that is created inside the window class, this Config seems to be a new separate instance. This is the part I don't understand. If someone could explain or give me some reference where to look I would greatly appreciate it.
This is some code very simplified
Window1.cs
<Window x:Class="Sample.UI.Main"
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:controller="clr-namespace:Sample.Controller"
mc:Ignorable="d"
Title="SampleApp" Height="600" Width="800" ResizeMode="NoResize" WindowStartupLocation="CenterScreen">
<Window.Resources>
<ResourceDictionary>
<controller:PublisherController x:Key="oController" />
</ResourceDictionary>
</Window.Resources>
<CheckBox x:Name="chkBoxShowRoom" Style="{StaticResource checkBoxTemplate}" Content="{StaticResource configShowRoom}" IsChecked="{Binding Source={StaticResource oController}, Path=Config.ShowRoom}"/>
Then my Window1.cs
public partial class Main : Window
{
public PublisherController Controller { get; set; }
Then Controller.cs
public class PublisherController
{
public Configuration Config { get; set; }
Then the Configuration.cs
public class Configuration : AbstractEntity, INotifyPropertyChanged
{
private bool _ShowRoom;
public bool ShowRoom
{
get
{
return _ShowRoom;
}
set
{
if (value != _ShowRoom)
{
this._ShowRoom = value;
OnPropertyChanged();
}
}
}
...