Change view from inside usercontrol - c#

My idea is to have a window with 2 content controls changing to different views. For example, my app may start with a welcome image in one content control and enter/exit button in other. On enter button click, I want to change the welcome view to some other view and the enter/exit buttons to 3 other buttons.
My current problem is, trying to change the welcome view by clicking the enter button does not seem to work if I put it in a user control and host it in content control. My guess is because I am actually creating a new instance of my MainViewModel instead of referencing the existing one, but I am not entirely sure.
How can I make it so, when I click on my button in BottomPanelTwoButtonsView my HomeView changes to TestView?
There are 4 views in total:
HomeView - the welcome screen
TestView - some other view
BottomPanelTwoButtonsView - enter/exit buttons
BottomPanelThreeButtonsView - some 3 other buttons
Here's the basic code I have:
App.xaml.cs
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow app = new MainWindow();
MainViewModel context = new MainViewModel();
app.DataContext = context;
app.Show();
}
}
MainWindow.xaml
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:localViewModels="clr-namespace:MyApp.ViewModels"
xmlns:localViews="clr-namespace:MyApp.Views"
mc:Ignorable="d"
Name="RootWindow"
WindowStyle="None" ResizeMode="NoResize"
Topmost="True"
WindowStartupLocation="CenterScreen"
WindowState="Maximized"
Background="{StaticResource BackgroundSolidColorBrush}"
ToolBarTray.IsLocked="True"
NumberSubstitution.Substitution="European"
Title="MyApp" Height="1920" Width="1080">
<Window.Resources>
<DataTemplate DataType="{x:Type localViewModels:HomeViewModel}">
<localViews:HomeView />
</DataTemplate>
<DataTemplate DataType="{x:Type localViewModels:TestViewModel}">
<localViews:TestView />
</DataTemplate>
<DataTemplate DataType="{x:Type localViewModels:BottomPanelTwoButtonsViewModel}">
<localViews:BottomPanelTwoButtons />
</DataTemplate>
<DataTemplate DataType="{x:Type localViewModels:BottomPanelThreeButtonsViewModel}">
<localViews:BottomPanelThreeButtons />
</DataTemplate>
</Window.Resources>
<DockPanel Background="White">
<ContentControl Content="{Binding CurrentBottomPanelViewModel}" DockPanel.Dock="Bottom" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" Height="96" Width="1080">
</ContentControl>
<ContentControl Content="{Binding CurrentPageViewModel}" HorizontalAlignment="Stretch" VerticalAlignment="Top" HorizontalContentAlignment="Stretch" Height="1824" Width="1080">
</ContentControl>
</DockPanel>
</Window>
MainViewModel.cs
public class MainViewModel : ObservableObject
{
#region Fields
private ICommand _changePageCommand;
private IPageViewModel _currentPageViewModel;
private IBottomPanelViewModel _currentBottomPanelViewModel;
private ObservableCollection<IPageViewModel> _pageViewModels;
#endregion
public MainViewModel()
{
CurrentPageViewModel = new HomeViewModel();
CurrentBottomPanelViewModel = new BottomPanelTwoButtonsViewModel();
//CurrentBottomPanelViewModel = new BottomPanelThreeButtonsViewModel();
PageViewModels.Add(new ScreeningsScheduleViewModel());
PageViewModels.Add(new TestViewModel());
PageViewModels.Add(CurrentPageViewModel);
}
#region Properties / Commands
public ICommand ChangePageCommand
{
get
{
if (_changePageCommand == null)
{
_changePageCommand = new RelayCommand(
p => ChangeViewModel((IPageViewModel)p),
p => p is IPageViewModel);
}
return _changePageCommand;
}
}
public ObservableCollection<IPageViewModel> PageViewModels
{
get
{
if (_pageViewModels == null)
{
_pageViewModels = new ObservableCollection<IPageViewModel>();
}
return _pageViewModels;
}
set
{
_pageViewModels = value;
OnPropertyChanged("PageViewModels");
}
}
public IPageViewModel CurrentPageViewModel
{
get => _currentPageViewModel;
set
{
if (_currentPageViewModel != value)
{
_currentPageViewModel = value;
OnPropertyChanged("CurrentPageViewModel");
}
}
}
public IBottomPanelViewModel CurrentBottomPanelViewModel
{
get => _currentBottomPanelViewModel;
set
{
if (_currentBottomPanelViewModel != value)
{
_currentBottomPanelViewModel = value;
OnPropertyChanged("CurrentBottomPanelViewModel");
}
}
}
#endregion
#region Methods
private void ChangeViewModel(IPageViewModel viewModel)
{
if (!PageViewModels.Contains(viewModel))
{
PageViewModels.Add(viewModel);
}
CurrentPageViewModel = PageViewModels
.FirstOrDefault(vm => vm == viewModel);
}
#endregion
}
BottomPanelTwoButtonsView.xaml
<UserControl x:Class="MyApp.Views.BottomPanelTwoButtons"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mvm="clr-namespace:MyApp"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="1080">
<UserControl.DataContext>
<mvm:MainViewModel/>
</UserControl.DataContext>
<Grid Height="96" Background="White" Width="1080">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Column="0"
Content="1"
Command="{Binding DataContext.ChangePageCommand, Source={RelativeSource Mode=FindAncestor, AncestorType={x:Type mvm:MainViewModel}}, Mode=TwoWay}"
CommandParameter="{Binding PageViewModels[2]}"
IsEnabled="True"
Style="{StaticResource RoundCornerButtonCyanFilledBig}"
/>
<Button Grid.Column="1" Style="{StaticResource RoundCornerButtonMagentaFilledBig}"/>
</Grid>
</UserControl>
IPageViewModel.cs
public interface IPageViewModel
{
string Name { get; }
}
ObservableObject.cs
public abstract class ObservableObject : INotifyPropertyChanged
{
[Conditional("DEBUG")]
[DebuggerStepThrough]
public virtual void VerifyPropertyName(string propertyName)
{
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (!ThrowOnInvalidPropertyName)
{
Debug.Fail(msg);
}
else
{
throw new Exception(msg);
}
}
}
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
public virtual void RaisePropertyChanged(string propertyName)
{
VerifyPropertyName(propertyName);
OnPropertyChanged(propertyName);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
RelayCommand.cs
public class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute)
: this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_execute = execute ?? throw new ArgumentNullException("execute");
_canExecute = canExecute;
}
[DebuggerStepThrough]
public bool CanExecute(object parameters)
{
return _canExecute == null ? true : _canExecute(parameters);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameters)
{
_execute(parameters);
}
}

I guess that your problem is within the BottomPanelTwoButtonsView.xaml
There you have the following lines:
<UserControl.DataContext>
<mvm:MainViewModel/>
</UserControl.DataContext>
So you're overwriting your datacontext which is defined in the Window.Resources of the MainWindow

Related

Why doesn't my view update when adding items to the ObservableCollection

So I have this project where I have two buttons and a ListView. The ListView is separated into it's own UserControl with it's own ViewModel which contains a ObservableCollection.
I'm using a ContentPresenter to display that control because I will be using different views.
Currently, when I'm clicking the Log button, it does in fact add the string to the collection, but the view doesn't update. And it keeps adding more and more everytime I click on it. (I've put a breakpoint inside private void AddItemOne() to inspect it to prove that it adds items.)
Question
Why doesn't my view update when I click the "Log" button even though it's adding items.
It does add the first item if I hardcode it like this.
public LogViewModel()
{
Logs = new ObservableCollection<string>();
Logs.Add("Test");
}
MainWindow.xaml
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Button Grid.Row="0" Grid.Column="0" Height="25" Content="Log"
Command="{Binding AddItemOneCommand}"/>
<Button Grid.Row="0" Grid.Column="1" Height="25" Content="Other"
Command="{Binding AddItemTwoCommand}"/>
<UserControl Content="{Binding CurrentView}" Grid.Row="1" Grid.ColumnSpan="2"/>
</Grid>
MainViewModel.cs
class MainViewModel
{
public RelayCommand AddItemOneCommand { get; set; }
public RelayCommand AddItemTwoCommand { get; set; }
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set
{
_currentView = value;
}
}
/* ViewModels */
public LogViewModel LogViewModel { get; set; }
public MainViewModel()
{
AddItemOneCommand = new RelayCommand(o => AddItemOne());
AddItemTwoCommand = new RelayCommand(o => AddItemTwo());
LogViewModel = new LogViewModel();
_currentView = LogViewModel;
}
private void AddItemOne()
{
LogViewModel.Logs.Add("Test");
}
private void AddItemTwo()
{
LogViewModel.Logs.Add("Test");
}
}
LogView.xaml
<UserControl.DataContext>
<local:LogViewModel/>
</UserControl.DataContext>
<Grid>
<ListView ItemsSource="{Binding Logs}" Background="Gray"/>
</Grid>
LogViewModel.cs
class LogViewModel
{
public ObservableCollection<string> Logs { get; set; }
public LogViewModel()
{
Logs = new ObservableCollection<string>();
}
}
Misc
App.xaml
<Application.Resources>
<DataTemplate DataType="{x:Type local:LogViewModel}">
<local:LogView/>
</DataTemplate>
</Application.Resources>
And the RelayCommand
public class RelayCommand : ICommand
{
private Action<object> execute;
private Func<object, bool> canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return this.canExecute == null || this.canExecute(parameter);
}
public void Execute(object parameter)
{
this.execute(parameter);
}
}
Remove this from LogView.xaml:
<UserControl.DataContext>
<local:LogViewModel/>
</UserControl.DataContext>
It creates another instance of the LogViewModel instead of using the one that you create in the MainViewModel.
You should also replace the UserControl in MainWindow.xaml with a ContentControl that binds to the CurrentView property:
<ContentControl Content="{Binding CurrentView}" Grid.Row="1" Grid.ColumnSpan="2" />

Changing views with a ListBoxItem rather than a Button?

I have a single page with a sidebar (Menu Items), top bar and a big main area. I can get my code to work if I make my sidebar items buttons. In my design I have a Listbox of items that's just text I don't want to use buttons.
Goal: Is to select a menu item in the left side bar and change the view in the main area.
I'm very new to MVVM and there is lots of good content on this site and youtube but I haven't found an example that uses text, it's always with buttons which doesn't help me.
I wish I could do something like this.
<ListBoxItem x:Name="Customer"
Command={Binding UpdateViewCommand}
CommandParameter = "Customer"
</ListBoxItem>
I'll explain what I have.
LeftSideBar.xaml
<Grid>
<ListBox IsSelected="True" SelectionMode="Single">
<ListBoxItem x:Name="Customer">
<StackPanel>
<TextBlock>Customers</TextBlock>
</StackPanel>
</ListBoxItem>
<ListBoxItem x:Name="Security">
<StackPanel>
<TextBlock>Security</TextBlock>
</StackPanel>
</ListBoxItem>
</ListBox>
</Grid>
LeftSideBar.xaml.cs
public partial class LeftSideBar : UserControl
{
public LeftSideBar()
{
InitializeComponent();
}
}
MainWindow.xaml
<Window.Resources>
<DataTemplate x:Name="homeViewTemplate" DataType="{x:Type viewmodels:HomeViewModel}">
<view:HomeView DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="customerViewTemplate" DataType="{x:Type viewmodels:CustomerViewModel}">
<view:CustomerView DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate x:Name="securityViewTemplate" DataType="{x:Type viewmodels:SecurityViewModel}">
<view:SecurityView DataContext="{Binding}"/>
</DataTemplate>
</Window.Resources>
<Grid>
<controls:TopBar></controls:TopBar>
<controls:LeftSideBar></controls:LeftSideBar>
<ContentControl Grid.Column="1" Content="{Binding SelectedViewModel}"/>
</Grid>
MainWindow.xml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
MainViewModel.cs
internal class MainViewModel : BaseViewModel
{
private BaseViewModel _selectedViewModel = new HomeViewModel();
public BaseViewModel SelectedViewModel
{
get { return _selectedViewModel; }
set { _selectedViewModel = value; }
}
}
UpdateViewCommand.cs
public class UpdateViewCommand : ICommand
{
private MainViewModel ViewModel;
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{throw new NotImplementedException();}
public void Execute(object parameter)
{throw new NotImplementedException();}
}
Here's a quick and dirty example on how you could do that, you will have to adjust it to your needs.
Just wrap your text inside a type with a command... or whatever else you see fit.
Code:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WpfApp1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
DataContext = new[]
{
new Item {Text = "abcd", Command = new RelayCommand(s => MessageBox.Show("abcd!"))},
new Item {Text = "efgh", Command = new RelayCommand(s => MessageBox.Show("efgh!"))},
new Item {Text = "ijkl", Command = new RelayCommand(s => MessageBox.Show("ijkl!"))}
};
}
private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e?.AddedItems?[0] is Item item)
{
item.Command.Execute(null);
}
}
}
public class Item
{
public string Text { get; set; }
public ICommand Command { get; set; }
}
public class RelayCommand : ICommand
{
private readonly Func<object, bool> _canExecute;
private readonly Action<object> _execute;
public RelayCommand(Action<object> execute) : this(execute, _ => true)
{
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object? parameter)
{
return _canExecute(parameter);
}
public void Execute(object? parameter)
{
_execute(parameter);
}
public event EventHandler? CanExecuteChanged;
}
}
XAML:
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ListBox SelectionChanged="Selector_OnSelectionChanged" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate DataType="local:Item">
<TextBlock Text="{Binding Text}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>

Binding User Control from second lib project not work - wpf mvvm

My task: I want to bind textbox and button.
Although I found many topics about it I cannot manage my problem.
I have project: Client with WPF application WITH DEFAULT XAML no BINDING, which takes context from MenuWindow project, which is library. Inside MenuWindow project I have User Control WPF called: MenuProgram.
<UserControl x:Class="MenuWindow.MenuProgram"
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:MenuWindow"
mc:Ignorable="d"
d:DesignHeight="550" d:DesignWidth="780">
<UserControl.DataContext>
<local:MenuViewModel/>
</UserControl.DataContext>
<Grid Background="#FF6F6FA4">
<Label x:Name="lblTitle" Content="GUI Export Revit Data" HorizontalAlignment="Left" Margin="277,31,0,0" VerticalAlignment="Top" Height="50" Width="258" FontSize="24" FontWeight="Bold"/>
<Label x:Name="lblPrtdPath" Content="File prtd path" HorizontalAlignment="Left" Margin="200,176,0,0" VerticalAlignment="Top"/>
<Label x:Name="lblXmlPath1" Content="File xml path1" HorizontalAlignment="Left" Margin="200,222,0,0" VerticalAlignment="Top"/>
<Label x:Name="lblXmlPath2" Content="File xml path2" HorizontalAlignment="Left" Margin="200,266,0,0" VerticalAlignment="Top"/>
<TextBox x:Name="tbxPrtd" HorizontalAlignment="Left" Height="23" Margin="302,176,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="268" Text="{Binding PrtdFilePath}"/>
<TextBox x:Name="tbxXml1" HorizontalAlignment="Left" Height="23" Margin="302,222,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="268" Text="{Binding XmlFilePath1}"/>
<TextBox x:Name="tbxXml2" HorizontalAlignment="Left" Height="23" Margin="302,266,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="268" Text="{Binding XmlFilePath2}"/>
<Button x:Name="SayHi" Content="Start" HorizontalAlignment="Left" Margin="302,450,0,0" VerticalAlignment="Top" Width="174" Height="84" FontSize="22" Command="{Binding SayHi}" />
<Button x:Name="btnAbout" Content="About" HorizontalAlignment="Left" Margin="705,496,0,0" VerticalAlignment="Top" Width="55" Height="38" Command="{Binding SayHi}"/>
</Grid>
so I have
<UserControl.DataContext>
<mv:MenuViewModel/>
</UserControl.DataContext>
and with textBoxs or button I want to use binding.
in codeBehind this User Control there is nothing but default initialization.
In Project Menu there are:
MenuArguments.cs with mapping:
public string PrtdFilePath { get; set; }
public string XmlFilePath1 { get; set; }
public string XmlFilePath2 { get; set; }
RelayCommand:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace MenuWindow
{
public class RelayCommand : ICommand
{
private readonly Func<Boolean> _canExecute;
private readonly Action _execute;
public RelayCommand(Action execute)
: this(execute, null)
{
}
public RelayCommand(Action execute, Func<Boolean> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add
{
if (_canExecute != null)
CommandManager.RequerySuggested += value;
}
remove
{
if (_canExecute != null)
CommandManager.RequerySuggested -= value;
}
}
public Boolean CanExecute(Object parameter)
{
return _canExecute == null ? true : _canExecute();
}
public void Execute(Object parameter)
{
_execute();
}
}
}
and MenuViewModel.cs
namespace MenuWindow
{
public class MenuViewModel : INotifyPropertyChanged
{
public string gowno;
public MenuArguments _menuArgumenty;
public string PrtdFilePath
{
get { return _menuArgumenty.PrtdFilePath; }
set
{
_menuArgumenty.PrtdFilePath = value;
OnPropertyChanged("PrtdFilePath");
}
}
public string XmlFilePath1
{
get { return _menuArgumenty.XmlFilePath1; }
set
{
_menuArgumenty.XmlFilePath1 = value;
OnPropertyChanged("XmlFilePath1");
}
}
public string XmlFilePath2
{
get { return _menuArgumenty.XmlFilePath2; }
set
{
_menuArgumenty.XmlFilePath2 = value;
OnPropertyChanged("XmlFilePath2");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public MenuViewModel()
{
_menuArgumenty = new MenuArguments();
}
public ICommand SayHi
{
get
{
return new RelayCommand(SayHiExcute, CanSayHiExcute);
}
}
private void SayHiExcute()
{
if (!MenuArgumentsExists(_menuArgumenty))
{
MessageBox.Show(string.Format("Hi {0} {1}!", _menuArgumenty.PrtdFilePath, _menuArgumenty.XmlFilePath1));
SavePerosn(_menuArgumenty);
}
else
{
MessageBox.Show(string.Format("Hey {0} {1}, you exists in our database!", _menuArgumenty.PrtdFilePath, _menuArgumenty.XmlFilePath1));
}
}
private void SavePerosn(MenuArguments _menuArgumenty)
{
//Some Database Logic
}
private bool CanSayHiExcute()
{
return !MenuArgumentsExists(_menuArgumenty);
}
private bool MenuArgumentsExists(MenuArguments _menuArgumenty)
{
//Some logic
return false;
}
}
}
When I start program debuger goes through binding properties. After window appears there is no reaction from binding. What do I do wrong? Please help me.
BR,
student Cenarius
Thanks for comments, answers to your comments:
#tabby - I want to bind textBoxes: PrtdFilePath, XmlFilePath1, XmlFilePath1 and button SayHi
#maulik kansara - You are right, I was trying some another methods and I didnt remove code. It should be only version with local.
#grek40 - My example works for one-project in solution for Window not for UserControl which is set in another project. Here is picture:
#mm8 - I expected by puting data ino textBoxes or clicking button to see breakpoint in:
public string PrtdFilePath
{
get { return _menuArgumenty.PrtdFilePath; }
set
{
_menuArgumenty.PrtdFilePath = value;
OnPropertyChanged("PrtdFilePath");
}
}
Finally, I think that code in XAML is problem. I was reading about parent-child relations with finding binding/viewmodel/path but I am confused and I dont know how to solve it. Please help me thanks You for all comments.
#grek40 here is Code in Main APP WPF, I add context from my MenuWindow.
This MainWindow WPF APP has default XAML.
public MainWindow()
{
InitializeComponent();
menuProgram = new MenuProgram();//User Control
sw = new SharedWindow();//WPF window
this.Close();
sw.Content = menuProgram.Content;// here I set context
sw.ShowDialog();
}
and XAML:
<Window x:Class="Client.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:Client"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
</Grid>
</Window>
Whole code with Your change:
public partial class MainWindow : Window
{
private SharedWindow sw;
private MenuProgram menuProgram;
public MainWindow()
{
InitializeComponent();
menuProgram = new MenuProgram();
SetForContext();
}
private void SetForContext()
{
sw = new SharedWindow();
this.Close();
sw.Content = menuProgram;
sw.ShowDialog();
}
You need to set the UserControl as window Content, not the Content of UserControl:
sw.Content = menuProgram;// here I set context
/* Bad: sw.Content = menuProgram.Content; */
Your DataContext is assigned to the UserControl itself, so if you move the Content tree to a different Parent, it will no longer have its old DataContext.

wpf bind event or command to function on item data context

I have some WPF code that looks like this
C#
namespace ItemEventTest
{
public partial class MainWindow : Window
{
public MainWindow()
{
DataContext = this;
MyItems = new ObservableCollection<MyItem>();
MyItems.Add(new MyItem { Name = "Otto" });
MyItems.Add(new MyItem { Name = "Dag" });
MyItems.Add(new MyItem { Name = "Karin" });
InitializeComponent();
}
public ObservableCollection<MyItem> MyItems { get; set; }
}
public class MyItem :INotifyPropertyChanged
{
private string m_name;
public string Name
{
get { return m_name; }
set
{
m_name = value;
OnPropertyChanged();
}
}
public void WhenMouseMove()
{
//Do stuff
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Xaml
<Window x:Class="ItemEventTest.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:ItemEventTest"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<ListBox ItemsSource="{Binding MyItems}">
<ListBox.ItemTemplate>
<DataTemplate DataType="local:MyItem">
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
What I now want to do is when the mouse moves over an item is to call WhenMouseMove on the object that is the source of that item.
I would like to have the function called directly on the object and not by first going through MainWindow or some view model connected to MainWindow. It feels like it should be possible because the data is bound that way but I haven't managed to find a description of how to do it.
If you are seeking solution in MVVM pattern
Edit: Add a reference to System.Windows.Interactivity dll (which is system defined if Blend or VS2015 installed)
then add following namespace xmlns:i="schemas.microsoft.com/expression/2010/interactivity‌​"
// xaml file
<Grid>
<ListBox ItemsSource="{Binding MyItems}">
<ListBox.ItemTemplate>
<DataTemplate DataType="local:MyItem">
<TextBlock Text="{Binding Name}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseEnter">
<i:InvokeCommandAction Command="{Binding MouseHoveredItemChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
// viewmodel (MyItem class)
public void WhenMouseMove()
{
//Do stuff
Console.WriteLine(Name);
}
private RelayCommand _MouseHoveredItemChangedCommand;
public RelayCommand MouseHoveredItemChangedCommand
{
get
{
if (_MouseHoveredItemChangedCommand == null)
{
_MouseHoveredItemChangedCommand = new RelayCommand(WhenMouseMove);
}
return _MouseHoveredItemChangedCommand;
}
}
// RelayCommand class
public class RelayCommand : ICommand
{
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private Action methodToExecute;
private Func<bool> canExecuteEvaluator;
public RelayCommand(Action methodToExecute, Func<bool> canExecuteEvaluator)
{
this.methodToExecute = methodToExecute;
this.canExecuteEvaluator = canExecuteEvaluator;
}
public RelayCommand(Action methodToExecute)
: this(methodToExecute, null)
{
}
public bool CanExecute(object parameter)
{
if (this.canExecuteEvaluator == null)
{
return true;
}
else
{
bool result = this.canExecuteEvaluator.Invoke();
return result;
}
}
public void Execute(object parameter)
{
this.methodToExecute.Invoke();
}
}

How do I set the datacontext to a class for a usercontrol in XAML?

I have the following class which implements INotifyPropertyChanged
updateNotify.cs
public class updateNotify : INotifyPropertyChanged
{
private DateTime? previousUpdate;
private DateTime? nextUpdate;
public event PropertyChangedEventHandler PropertyChanged;
public updateNotify()
{
}
public updateNotify(DateTime? value)
{
this.previousUpdate = value;
}
public DateTime? GASpreviousUpdate
{
get { return previousUpdate; }
set
{
previousUpdate = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("GASpreviousUpdate");
}
}
public DateTime? GASnextUpdate
{
get { return nextUpdate; }
set
{
nextUpdate = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("GASnextUpdate");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
MainWindow.xaml.cs
public partial class MainWindow
{
private updateNotify properties = new updateNotify();
'''
public void start_dispatcherTimer()
{
dispatcherTimer.Stop();
DateTime timeNow = DateTime.Now;
properties.GASpreviousUpdate = timeNow;
properties.GASnextUpdate = timeNow.AddMinutes((double)Properties.Settings.Default.GASInterval);
dispatcherTimer.Start();
}
}
then in XAML
<UserControl x:Class="ProjectXYZ.Content.MainData"
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:DesignWidth="300" Height="400" >
....
<StackPanel>
<Label Content="Previous Refresh" Target="{Binding ElementName=PreviousRefresh}"/>
<TextBox x:Name="PreviousRefresh" Width="140" Text="{Binding Path=GASpreviousUpdate, Mode=OneWay}"/>
</StackPanel>
<StackPanel>
<Label Content="Next Refresh" Target="{Binding ElementName=NextRefresh}"/>
<TextBox x:Name="NextRefresh" Width="140" Text="{Binding Path=GASnextUpdate, Mode=OneWay}"/>
</StackPanel>
</UserControl>
but the textboxes are not updating in the UI for Text="{Binding Path=GASpreviousUpdate, Mode=OneWay}" and Text="{Binding Path=GASnextUpdate, Mode=OneWay}"
I think I need to set the datacontext but not sure how to do this in the usercontrol XAML. Please can someone point me in the right direction?
try:
public partial class MainWindow
{
public MainWindow()
{
this.DataContext = this.properties; // <----
}
}
I wouldn't call the field properties

Categories