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.
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?
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 am new to MVVM and WPF. I have tried to to bind data to a textbox using DataContext.
Model: MyMessage.cs
public class MyMessage : INotifyPropertyChanged
{
private string testMessage;
public string TestMessage
{
get { return testMessage; }
set
{
testMessage = value;
OnPropertyChanged("TestName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
ViewModel: MainViewModel.cs
class MainViewModel
{
MyMessage myMessage;
public MainViewModel()
{
myMessage = new MyMessage();
myMessage.TestMessage="Hai";
}
View : MainWindow.xaml
<Window x:Class="DemoApp2.Views.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>
<TextBox HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding TestMessage}" VerticalAlignment="Top" Width="120"/>
</Grid>
You need to turn "myMessage" into property, and bind it as MyMessage.TestMessage in your TextBox, assuming you bind MainViewModel as DataContext in Window.
Try this:
class MainViewModel
{
private MyMessage _messageProperty;
public MyMessage MessageProperty
{
get { return _messageProperty; }
set { _messageProperty = value; }
}
public MainViewModel()
{
_messageProperty = new MyMessage();
_messageProperty.TestMessage="Hai";
}
Also the string in your OnPropertyChanged event must be the same name as the property, like this:
public string TestMessage
{
get { return testMessage; }
set
{
testMessage = value;
OnPropertyChanged("TestMessage");
}
}
In the code-behind file of your MainWindow.xaml.cs, set the data context to your ViewModel:
class MainWindow
{
public MainWindow()
{
this.DataContext = new MainViewModel();
}
And in your MainWindow.xaml file, you have to refer to the nested property of your MessageProperty
<Window x:Class="DemoApp2.Views.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>
<TextBox HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" Text="{Binding MessageProperty.TestMessage}" VerticalAlignment="Top" Width="120"/>
</Grid>
Let me know if it worked and if you need more informations ;-)
Also I recommend you to make a Quickstart Tutorial of how MVVM works and how it's implemented e.g. http://www.codeproject.com/Articles/165368/WPF-MVVM-Quick-Start-Tutorial
What is the best way to acchieve this, what I am going to describe bellow.
I have two textboxes with twoway bindings on the same object and same property.
Now, when I update text in one textbox I wish other textbox to grab the same value again from object. Is that even possible, or I have to do this manually. For an example, I can use TextChanged event and set this value.
Yes you can bind a single property to two controls
If this class is your DataContext (viewmodel)
public class Bind : INotifyPropertyChanged
{
private string _text1;
public string text1
{
get
{
return _text1;
}
set
{
_text1=value;
NotifyPropertyChanged("text1");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
}
In XAML
<UserControl x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="350" Width="525"
xmlns:ViewModel="clr-namespace:WpfApplication1">
<UserControl.DataContext>
<ViewModel:Class1/>
</UserControl.DataContext>
<Grid>
<TextBox Width="150" Height="50" Text="{Binding text1, Mode=TwoWay}"/>
<TextBox Text="{Binding text1, Mode=TwoWay}" Margin="0,232,0,0"/>
</Grid>
</UserControl>