I'm trying to create a UWP stopwatch of sorts, but I'm stuck on updating the UI.
Below is my base stopwatch class in my Models folder
public class KuStopWatch : INotifyPropertyChanged
{
public string Name { get; set; }
public DateTime DateCreated = DateTime.UtcNow;
public Guid Id = Guid.NewGuid();
public List<TimeSpan> Laps;
public Stopwatch SwInstance = new Stopwatch();
public TimeSpan SavedState { get; set; }
public DateTime TimeStarted { get; set; }
private string _elapsed;
public string Elapsed
{
get { return _elapsed; }
set { _elapsed = value;
NotifyPropertyChanged();
}
}
public bool ClosedWhileRunning { get; set; }
public void Start()
{
TimeStarted = DateTime.UtcNow;
var tmr = new Timer() { Interval = 16, AutoReset = true };
tmr.Start();
tmr.Elapsed += (s, e) => {
Elapsed = ElapsedString();
};
SwInstance.Start();
}
...
public string ElapsedString()
{
var time = SwInstance.Elapsed + SavedState;
return time.ToString(#"hh\:mm\:ss\:ff");
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
This is my ""ViewModel""
public class StopwatchViewModel : ViewModelBase
{
public ObservableCollection<KuStopWatch> SWData { get; } = new ObservableCollection<KuStopWatch>();
public StopwatchViewModel()
{
//Dummy
SWData.Add(new KuStopWatch());
SWData[0].Start();
}
}
and my XAML
<Page
x:Class="MyApp.Views.StopwatchPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="using:MyApp.Models"
Style="{StaticResource PageStyle}"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="StopwatchTemplate" x:DataType="models:KuStopWatch">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="1"
Style="{ThemeResource BaseTextBlockStyle}"
Text="{Binding Path=Elapsed, UpdateSourceTrigger=PropertyChanged}" />
<Button
Grid.Row="2"
Grid.Column="1"
Style="{StaticResource ButtonRevealStyle}">
<SymbolIcon Symbol="Play" />
</Button>
</Grid>
</DataTemplate>
</Page.Resources>
<Grid x:Name="ContentArea" Margin="{StaticResource MediumLeftRightMargin}">
<controls:AdaptiveGridView
x:Name="StopwatchViewGrid"
Background="Transparent"
ItemTemplate="{StaticResource StopwatchTemplate}"
ItemsSource="{Binding}"
OneRowModeEnabled="False" />
</Grid>
The data context is set to ViewModel.SWData.
The current behavior I get is the Time in the UI only updating when I switch pages back and forth. And the way I messed up my INotify... results in "The application called an interface that was marshalled for a different thread." exceptions, no matter what I try. Does anyone know the correct way to do this?
INotifyPropertyChanged is implemented as it should. The problem is the Timer. Use a DispatcherTimer, it uses the UI Thread and not a thread pool, like the Timer
Related
I've been digging back into some projects in WPF, and come across a hurdle that I haven't been able to find a directly related solution for.
Essentially I want to filter a child property of a SelectedItem dynamically (via text entered in the filter box, something along the lines of .Contains(filter)). The UI displays correctly in the sample project, but after attempting to implement solutions from every hit possible on SO or otherwise, I've come up blank, or making serious compromises to the MVVM pattern.
ParentItem:
public class ParentItem
{
public string Name { get; set; }
public List<string> ChildItems { get; set; }
public DateTime CreatedOn { get; set; }
public bool IsActive { get; set; }
public ParentItemStatus Status { get; set; }
}
public enum ParentItemStatus
{
Status_One,
Status_Two
}
ViewModel:
public class MainWindowViewModel : ViewModelBase
{
public ObservableCollection<ParentItem> ParentItems { get; set; }
public MainWindowViewModel()
{
ParentItems = new ObservableCollection<ParentItem>();
LoadDummyParentItems();
}
private ICommand _filterChildrenCommand;
public ICommand FilterChildrenCommand => _filterChildrenCommand ?? (_filterChildrenCommand = new RelayCommand(param => FilterChildren((string)param), param => CanFilterChildren((string)param)));
private bool CanFilterChildren(string filter)
{
//TODO: Check for selected item in real life.
return filter.Length > 0;
}
private void FilterChildren(string filter)
{
//TODO: Filter?
}
private void LoadDummyParentItems()
{
for (var i = 0; i < 20; i++)
{
ParentItems.Add(new ParentItem()
{
Name = $"Parent Item {i}",
CreatedOn = DateTime.Now.AddHours(i),
IsActive = i % 2 == 0 ? true : false,
Status = i % 2 == 0 ? ParentItemStatus.Status_Two : ParentItemStatus.Status_One,
ChildItems = new List<string>() { $"Child one_{i}", $"Child two_{i}", $"Child three_{i}", $"Child four_{i}" }
});
}
}
}
MainWindow:
<Window x:Class="FilteringDemo.Views.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:FilteringDemo.Views"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<CollectionViewSource x:Key="ChildItemsViewSource" Source="{Binding ElementName=ItemList, Path=SelectedItem.ChildItems}" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".25*"/>
<ColumnDefinition Width=".75*"/>
</Grid.ColumnDefinitions>
<ListView x:Name="ItemList" Grid.Column="0" Margin="2" ItemsSource="{Binding ParentItems}" SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding ElementName=ItemList, Path=SelectedItem.Name}" Margin="2"/>
<TextBlock Grid.Column="1" Text="{Binding ElementName=ItemList, Path=SelectedItem.CreatedOn}" Margin="2"/>
<TextBlock Grid.Column="2" Text="{Binding ElementName=ItemList, Path=SelectedItem.IsActive}" Margin="2"/>
<TextBlock Grid.Column="3" Text="{Binding ElementName=ItemList, Path=SelectedItem.Status}" Margin="2"/>
</Grid>
<ListView Grid.Row="1" Margin="2" ItemsSource="{Binding Source={StaticResource ChildItemsViewSource}}" />
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="Contains:" Margin="2" VerticalAlignment="Center"/>
<TextBox x:Name="ChildFilterInput" Grid.Column="1" Margin="2" />
<Button Grid.Column="2" Content="Filter" Width="100" Margin="2" Command="{Binding FilterChildrenCommand}" CommandParameter="{Binding ElementName=ChildFilterInput, Path=Text}"/>
</Grid>
</Grid>
</Grid>
</Window>
I've tried various implementations of adding a Filter event handler on the CollectionViewSource but have been unable to make them dynamic. It also seems like most examples/tutorials only deal directly with the parent item or static filters.
In a non-MVVM mindset, I was thinking to have an interaction trigger drive the selected item back into the ViewModel, and then create a filtered ICollectionView which the ChildItems ListView would bind to, but it seems like I can't be the only person trying this, and that there must be an easier MVVM binding friendly way.
The following example shows a simple solution to implement live filtering on a collection:
Person.cs
class Person
{
public Person(string firstName, string lastName)
{
this.FirstName = firstName;
this.LastName = lastName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}
ViewModel.cs
class ViewModel
{
public ViewModel()
{
this.Persons = new ObservableCollection<Person>()
{
new Person("Derek", "Zoolander"),
new Person("Tony", "Montana"),
new Person("John", "Wick"),
new Person("The", "Dude"),
new Person("James", "Bond"),
new Person("Walter", "White")
};
}
private void FilterData(string filterPredicate)
{
// Execute live filter
CollectionViewSource.GetDefaultView(this.Persons).Filter =
item => (item as Person).FirstName.StartsWith(filterPredicate, StringComparison.OrdinalIgnoreCase);
}
private string searchPredicate;
public string SearchPredicate
{
get => this.searchFilter;
set
{
this.searchPredicate = value;
FilterData(value);
}
}
public ObservableCollection<Person> Persons { get; set; }
}
MainWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<StackPanel>
<TextBox Text="{Binding SearchPredicate, UpdateSourceTrigger=PropertyChanged"} />
<ListView ItemsSource="{Binding Persons}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Firstname" DisplayMemberBinding="{Binding FirstName}" />
<GridViewColumn Header="Lastname" DisplayMemberBinding="{Binding LastName}" />
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</Window>
Update
It seems like you are having problems to filter the child items. The following example is more specific to your scenario:
DataItem.cs
class DataItem
{
public DataItem(string Name)
{
this.Name = name;
}
public string Name { get; set; }
public ObservableCollection<DataItem> ChildItems { get; set; }
}
ViewModel.cs
class ViewModel
{
public ViewModel()
{
this.ParentItems = new ObservableCollection<DataItem>()
{
new DataItem("Ben Stiller") { ChildItems = new ObservableCollection<DataItem>() { new DataItem("Zoolander"), new DataItem("Tropical Thunder") }},
new DataItem("Al Pacino") { ChildItems = new ObservableCollection<DataItem>() { new DataItem("Scarface"), new DataItem("The Irishman") }},
new DataItem("Keanu Reeves") { ChildItems = new ObservableCollection<DataItem>() { new DataItem("John Wick"), new DataItem("Matrix") }},
new DataItem("Bryan Cranston") { ChildItems = new ObservableCollection<DataItem>() { new DataItem("Breaking Bad"), new DataItem("Malcolm in the Middle") }}
};
}
private void FilterData(string filterPredicate)
{
// Execute live filter
CollectionViewSource.GetDefaultView(this.SelectedParentItem.ChildItems).Filter =
item => (item as DataItem).Name.StartsWith(filterPredicate, StringComparison.OrdinalIgnoreCase);
}
private string searchPredicate;
public string SearchPredicate
{
get => this.searchFilter;
set
{
this.searchPredicate = value;
FilterData(value);
}
}
public ObservableCollection<DataItem> ParentItems { get; set; }
public DataItem SelectedParentItem { get; set; }
}
MainWindow.xaml
<Window>
<Window.DataContext>
<ViewModel />
</Window.DataContext>
<StackPanel>
<ListView ItemsSource="{Binding ParentItems}"
SelectedItem="{Binding SelectedParentItem}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBox Text="{Binding SearchPredicate, UpdateSourceTrigger=PropertyChanged}" />
<ListView ItemsSource="{Binding SelectedParentItem.ChildItems}" />
</StackPanel>
</Window>
Using the examples from #BionicCode- I added a SelectedParentItem INPC property to the ViewModel, performed the filtering on that via CollectionViewSource.Filter, and bound the ChildItems ListView to SelectedParentItem.ChildItems.
I did not bind the text box property changed to a backing field in the VM as per #BionicCode's example, as the "real" ChildItems might be in the mid 10,000's and I didn't want it to filter on each keystroke. So this answer implements the filter button and text box command, and the CanFilterChildren is doing a proper null check.
MainWindowViewModel.cs:
public class MainWindowViewModel : ViewModelBase
{
public ObservableCollection<ParentItem> ParentItems { get; set; }
private ParentItem _selectedParentItem;
public ParentItem SelectedParentItem
{
get { return _selectedParentItem; }
set { SetProperty(ref _selectedParentItem, value); }
}
public MainWindowViewModel()
{
ParentItems = new ObservableCollection<ParentItem>();
LoadDummyParentItems();
}
private ICommand _filterChildrenCommand;
public ICommand FilterChildrenCommand => _filterChildrenCommand ?? (_filterChildrenCommand = new RelayCommand(param => FilterChildren((string)param), param => CanFilterChildren((string)param)));
private bool CanFilterChildren(string filter) => SelectedParentItem != null && filter.Length > 0;
private void FilterChildren(string filter)
{
CollectionViewSource.GetDefaultView(SelectedParentItem.ChildItems).Filter = item => (item as string).Contains(filter);
}
private void LoadDummyParentItems()
{
for (var i = 0; i < 20; i++)
{
ParentItems.Add(new ParentItem()
{
Name = $"Parent Item {i}",
CreatedOn = DateTime.Now.AddHours(i),
IsActive = i % 2 == 0 ? true : false,
Status = i % 2 == 0 ? ParentItemStatus.Status_Two : ParentItemStatus.Status_One,
ChildItems = new List<string>() { $"Child one_{i}", $"Child two_{i}", $"Child three_{i}", $"Child four_{i}" }
});
}
}
}
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
private readonly MainWindowViewModel _viewModel;
public MainWindow()
{
InitializeComponent();
_viewModel = new MainWindowViewModel();
this.DataContext = _viewModel;
}
}
MainWindow.xaml:
<Window x:Class="FilteringDemo.Views.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:FilteringDemo.Views"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".25*"/>
<ColumnDefinition Width=".75*"/>
</Grid.ColumnDefinitions>
<ListView x:Name="ItemList" Grid.Column="0" Margin="2" ItemsSource="{Binding ParentItems}" SelectedItem="{Binding SelectedParentItem, Mode=OneWayToSource}" SelectionMode="Single">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="1*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding ElementName=ItemList, Path=SelectedItem.Name}" Margin="2"/>
<TextBlock Grid.Column="1" Text="{Binding ElementName=ItemList, Path=SelectedItem.CreatedOn}" Margin="2"/>
<TextBlock Grid.Column="2" Text="{Binding ElementName=ItemList, Path=SelectedItem.IsActive}" Margin="2"/>
<TextBlock Grid.Column="3" Text="{Binding ElementName=ItemList, Path=SelectedItem.Status}" Margin="2"/>
</Grid>
<ListView Grid.Row="1" Margin="2" ItemsSource="{Binding SelectedParentItem.ChildItems}" />
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBox x:Name="ChildFilterInput" Grid.Column="0" Margin="2">
<TextBox.InputBindings>
<KeyBinding Command="{Binding FilterChildrenCommand}" CommandParameter="{Binding ElementName=ChildFilterInput, Path=Text}" Key="Return" />
</TextBox.InputBindings>
</TextBox>
<Button Grid.Column="1" Content="Filter" Width="100" Margin="2" Command="{Binding FilterChildrenCommand}" CommandParameter="{Binding ElementName=ChildFilterInput, Path=Text}"/>
</Grid>
</Grid>
</Grid>
</Window>
ViewModelBase.cs:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName] string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, newValue))
{
field = newValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
return true;
}
return false;
}
}
RelayCommand.cs:
public class RelayCommand : ICommand
{
private Predicate<object> _canExecuteMethod;
private Action<object> _executeMethod;
public RelayCommand(Action<object> executeMethod, Predicate<object> canExecuteMethod = null)
{
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter)
{
return _canExecuteMethod == null ? true : _canExecuteMethod(parameter);
}
public void Execute(object parameter)
{
_executeMethod(parameter);
}
}
ParentItem.cs:
public class ParentItem
{
public string Name { get; set; }
public List<string> ChildItems { get; set; }
public DateTime CreatedOn { get; set; }
public bool IsActive { get; set; }
public ParentItemStatus Status { get; set; }
}
public enum ParentItemStatus
{
Status_One,
Status_Two
}
App.xaml:
<Application x:Class="FilteringDemo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:FilteringDemo"
StartupUri="Views/MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
Note: The MainWindow.xaml file was moved into a "Views" folder, so I'm including the App.xaml with the updated StartupUri in case anyone is trying to copy and paste.
I'm writing my first "real" MVVM application,So i need help for this situation,so i have usercontrol and page and mainwindow,the mainwindow have a frame holds page,usercontrol into the page i assign the same datacontext on Mainwindow and page,for usercontrol i put another datacontext but doesn't work, i need to know what i miss.
Here is my page Xaml code
<Page x:Class="BudG.Pages.SreachForIdentity"
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:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:BudG.Pages"
xmlns:UserControls="clr-namespace:BudG.UserControls"
xmlns:ViewModel="clr-namespace:BudG.ViewsModel"
Width="{Binding ActualWidth,
RelativeSource = {RelativeSource AncestorType = {x:Type Window}}}"
Height="{Binding ActualHeight,
RelativeSource ={RelativeSource AncestorType = {x:Type Window}}}"
Title="SreachForIdentity" x:Name="SearchIdentity" Loaded="SearchIdentity_Loaded">
<Grid x:Name="LayoutRoot">
<UserControls:SearchForIdentityView DataContext="{Binding LogInViewModel}"/>
</Grid>
</Page>
and here is my SearchForIdentityView "UserControl" Xaml Code
<UserControl x:Class="BudG.UserControls.SearchForIdentityView"
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:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:BudG.UserControls">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="5"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="5"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock x:Name="LblTitle" Grid.Row="1" Grid.Column="1" Text="Find your account." Padding="10" Height="40" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="16" Foreground="#FF747070" FontFamily="Times New Roman" />
<xctk:WatermarkTextBox x:Name="TxtNationalID" Grid.Row="3" Grid.Column="1" Width="300" Watermark="Enter new National Id" Padding="8" Style="{DynamicResource WatermarkTextBoxStyle1 }" Cursor="IBeam" />
<Button Height="40" Content="Search" Grid.Row="7" Grid.Column="1" Width="150" Style="{DynamicResource StyleOfSaveButton}" VerticalAlignment="Top" Margin="89.5,0,89.5,0" Cursor="Hand" Command="{Binding LogInCommand}"/>
</Grid>
</UserControl>
here is the UserControl code behind
public partial class SreachForIdentityView : UserControl
{
public SreachForIdentityView()
{
InitializeComponent();
}
}
and the loginviewmodel code
public class LogInViewModel : ViewModelBase, ILogInViewModel
{
private IEventAggregator _eventAggregator;
private IUserRepository _userRepository;
public LogInViewModel(IUserRepository userRepository, IEventAggregator eventAggregator)
{
_userRepository = userRepository;
_eventAggregator = eventAggregator;
LogInCommand=new DelegateCommand(OnLogInExecute);
}
private void OnLogInExecute()
{
Messagebox.Show("done");
}
public async Task LoadAsyncByPassword(string UserName, string password)
{
var user = await _userRepository.GetAsyncByPassword(UserName, password);
if(user==null)
{
_eventAggregator.GetEvent<OpenDetailViewEvent>().Publish(0);
}
else
{ _eventAggregator.GetEvent<OpenDetailViewEvent>().Publish(user.IdAccount); }
}
public ICommand LogInCommand{get;set;}
}
and here is the MainViewModel Code
public IEventAggregator _eventaggregator;
private Func<IUserAccountViewModel> _useraccountviewmodelCreator;
private IUserAccountViewModel _useraccountViewModel;
private ILogInViewModel _LoginViewModel;
private int _checkid;
private Func<ILogInViewModel> _loginViewModelCreator;
private Page _PageToNavigate;
private Func<INotificationViewModel> _notificationViewModel;
public MainViewModel(INavigationViewModel navigationveiwmodel,
Func<IUserAccountViewModel> useraccountviewmodelCreator,
Func<ILogInViewModel> loginViewModelCreator, Func<INotificationViewModel> notificationViewModel
, IEventAggregator EventAggregator)
{
_useraccountviewmodelCreator = useraccountviewmodelCreator;
_loginViewModelCreator = loginViewModelCreator;
_eventaggregator = EventAggregator;
_notificationViewModel = notificationViewModel;
_eventaggregator.GetEvent<OpenDetailViewEvent>()
.Subscribe(OnOpenDetailView);
_eventaggregator.GetEvent<AfterUserSavedEvent>()
.Subscribe(AfterUserSaved);
NavigationViewModel = navigationveiwmodel;
CreateNewUserCommand = new DelegateCommand(OnCreateNewUserExecute);
ForgetPassword = new DelegateCommand(OnForgetPassword);
}
private void OnForgetPassword()
{
PageToNavigate = new Pages.SreachForIdentity(this);
}
public INavigationViewModel NavigationViewModel { get; set; }
public ICommand CreateNewUserCommand { get; set; }
public ICommand ForgetPassword { get; set; }
public string UserName { get; set; }
public string Password { get; set; }
public bool Result { get; set; }
public Page PageToNavigate
{
get { return _PageToNavigate; }
set { _PageToNavigate = value; OnPropertyChanged(); }
}
public int CheckID
{
get { return _checkid; }
set { _checkid = value; OnPropertyChanged(); }
}
public ILogInViewModel LogInViewModel
{
get { return _LoginViewModel; }
set { _LoginViewModel = value; OnPropertyChanged(); }
}
public IUserAccountViewModel UserAccountViewModel
{
get { return _useraccountViewModel; }
set
{
_useraccountViewModel = value;
OnPropertyChanged();
}
}
private INotificationViewModel _NotificationViewModel;
public INotificationViewModel NotificationViewModel
{
get { return _NotificationViewModel; }
set { _NotificationViewModel = value; OnPropertyChanged(); }
}
}
}
So the Command In the button doesn't work at all and i don't know why?
The Answer is i must assign an instance to LogInViewModel property to work like this
LogInViewModel = _viewmodel._loginViewModelCreator();
or
LogInViewModel = new LogInViewModel();
Thank you for #Clemens for helping me, and guide me to answer
I have a DataGrid which is using an ObservableCollection as the ItemsSource.
The items in the collection are items of a custom class.
When I edit one of those items, I can't get the view to update no matter what I do. I have tried removing and re-adding all of the items to the collection, and I have tried re-assigning the collection to the grid.
I know the edits are being saved correctly because I can see the changed values in the debugger.
What could possibly be causing this to happen?
Is there some other way I could force the grid to refresh?
As noted in the ObservableCollection documentation, only insertions and deletions are notified in the collection, which is exposed by the CollectionChanged event. In order to make the items in the ObservableCollection notify a change has been made, the underlying model must implement INotifyPropertyChanged:
Account:
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace TestUWP
{
public class Account : INotifyPropertyChanged
{
private string _accountName;
private decimal _amount;
public string AccountName
{
get => _accountName;
set
{
_accountName = value;
OnPropertyChanged();
}
}
public decimal Amount
{
get => _amount;
set
{
_amount = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
ViewModel + RelayCommand:
using System;
using System.Collections.ObjectModel;
using System.Windows.Input;
namespace TestUWP
{
public class AccountViewModel
{
public AccountViewModel()
{
Accounts = new ObservableCollection<Account>
{
new Account {AccountName = "Account 1", Amount = 1000M},
new Account {AccountName = "Account 2", Amount = 2000M},
new Account {AccountName = "Account 3", Amount = 3000M},
};
AddAccountCommand = new RelayCommand(AddAccount);
EditAccountCommand = new RelayCommand(EditAccount);
}
public ICommand AddAccountCommand { get; }
public ICommand EditAccountCommand { get; }
public ObservableCollection<Account> Accounts { get; }
private void AddAccount()
{
Accounts.Add(new Account{AccountName = $"Account {Accounts.Count+1}", Amount = 1000M * (Accounts.Count+1)});
}
private void EditAccount()
{
Accounts[Accounts.Count - 1].Amount += 200M;
}
}
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public event EventHandler CanExecuteChanged;
public RelayCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;
public void Execute(object parameter) => _execute();
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
MainPage:
<Page
x:Class="TestUWP.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TestUWP"
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>
<local:AccountViewModel />
</Page.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.5*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ListView Grid.Column="0" Grid.RowSpan="2" ItemsSource="{Binding Accounts}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="x:String">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding AccountName}" Grid.Column="0" FontSize="30"/>
<TextBlock Text="{Binding Amount}" Grid.Column="1" Margin="30,0,0,0" FontSize="30"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Button Grid.Column="1" Grid.Row="0" Content="Add" FontSize="50" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Command="{Binding AddAccountCommand}"/>
<Button Grid.Column="1" Grid.Row="1" Content="Edit" FontSize="50" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Command="{Binding EditAccountCommand}"/>
</Grid>
</Page>
Changing Account from a POCO to one that implements the INotifyPropertyChanged allows the UI to refresh the Amount whenever the Edit button is clicked.
Alternatively, deleting and reinserting an item will also update the ItemSource, however this is not advisable due to the item will be appended to the end of the collection, and will then need custom sorting logic to say the least.
So I have got a view with a label and I have got a ViewModel for it.
ViewModelBase
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual bool SetAndRaisePropertyChanged<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(storage, value))
return false;
storage = value;
this.RaisePropertyChanged(propertyName);
return true;
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
This is how the ViewModel looks like:
private string _balance = "1111$";
public string Balance
{
get { return _balance; }
set { SetAndRaisePropertyChanged(ref _balance, value); }
}
And here is the view:
<UserControl x:Class="monei_project.MainUpperView"
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:project"
xmlns:vm="clr-namespace:project.ViewModels"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="2200" FontFamily="Open Sans">
<UserControl.Resources>
<vm:MainUpperViewModel x:Key="MainUpperViewModel"/>
</UserControl.Resources>
<Grid DataContext="{Binding Source={StaticResource MainUpperViewModel}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Rectangle Grid.RowSpan="1" Grid.ColumnSpan="22" Fill="#013542"></Rectangle>
<Label Grid.Column="6" Grid.ColumnSpan="2" VerticalAlignment="Center" Foreground="White" FontSize="16">Balance:</Label>
<Label x:Name="lblBalance" Grid.Column="7" Grid.ColumnSpan="5" VerticalAlignment="Center" Foreground="White" FontFamily="Open Sans SemiBold" FontSize="24" Margin="55,28,45,33">
<Label.Content>
<Binding Path="Balance"/>
</Label.Content>
</Label>
</Grid>
In the designer, I can see the content of the label
But when I run the application, the label is empty
What is the problem?
I have already created some ViewModel, but there I worked with textboxes. We used INotifyPropertyChanged interface, and I am not sure how does it work, so my guess was, that it sets the content, but won't display it, because the label is not updating, so I tried to use the OnPropertyChanged function with a PropertyChangedEventHandler, what we used earlier to the other ViewModels, but it didn't work either, I do not know what can be wrong.
There are some frameworks providing class already implementing the needed interfaces, if you want to do it yourself, here is a possibility:
First you have your ViewModelBase and all your ViewModels should inherit it
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual bool SetAndRaisePropertyChanged<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(storage, value))
return false;
storage = value;
this.RaisePropertyChanged(propertyName);
return true;
}
}
then in your viewmodel you will declare your property like this:
private String _mBalance;
public String Balance
{
get { return _mBalance; }
set => SetAndRaisePropertyChanged(ref _mBalance, value);
}
[EDIT]: I want to keep the history of the answer, so check my edit below with full fonctionnal example:
Usually I split in more files, but i wanted to stay simple, so you need 2 files (I'm trying to apply MVVM pattern so i'm adding directories):
- Views\MainWindow.xaml
- ViewModels\MainWindowViewModel.cs
Views\MainWindow.xaml:
<Window x:Class="StackOverflow_DBG.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:StackOverflow_DBG"
xmlns:viewmodels="clr-namespace:StackOverflow_DBG.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="100" Width="400">
<Window.DataContext>
<viewmodels:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="1" Grid.Column="0" Content="{Binding LabelTxt}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding ValueTxt}"/>
<Button Grid.Row="1" Grid.Column="2" Content="Change Label" Command="{Binding ChangeLabel}"/>
</Grid>
</Window>
ViewModels\MainWindowViewModel.cs:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace StackOverflow_DBG.ViewModels
{
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual bool SetAndRaisePropertyChanged<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(storage, value))
return false;
storage = value;
this.RaisePropertyChanged(propertyName);
return true;
}
}
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();
}
}
class MainWindowViewModel : ViewModelBase
{
private String m_LabelTxt = "Foo";
public String LabelTxt
{
get { return m_LabelTxt; }
set => SetAndRaisePropertyChanged(ref m_LabelTxt, value);
}
private String m_ValueTxt;
public String ValueTxt
{
get { return m_ValueTxt; }
set => SetAndRaisePropertyChanged(ref m_ValueTxt, value);
}
private RelayCommand m_ChangeLabel;
public RelayCommand ChangeLabel
{
get { return m_ChangeLabel; }
set { m_ChangeLabel = value; }
}
public MainWindowViewModel()
{
ChangeLabel = new RelayCommand(() => {
if (LabelTxt == "Foo")
{
LabelTxt = "Bar ";
}
else
{
LabelTxt = "Foo ";
}
});
}
}
}
This way you also see how to bind button for example. Press the button to see that the update is well done.
If using same directories than me, remember to edit app.xaml to use StartupUri="Views/MainWindow.xaml">
instead of
StartupUri="MainWindow.xaml">
Have you properly set the DataContext on the window/control to your view model? You need to set your DataContext before you are able to use bindings. And as such, you should probably use the proper way for binding:
<Label Content="{Binding Balance}" ... />
Edit:
Okay, I'll give you a concrete example of what I mean. Also, you're going to run into a lot of issues using a view model as a StaticResource. Why do I say this? Because once you start adding dependencies to your view model (accessing business logic, etc), you will need some form of dependency injection (DI) or a similar way to do so.
So you have your ViewModelBase, so I'll use that and not duplicate myself. Here's a simple view model:
public class AccountViewModel : ViewModelBase
{
string _balance = "1111$";
public AccountViewModel(string accountNumber)
{
AccountNumber = accountNumber;
}
public string AccountNumber { get; }
public string Balance
{
get { return _balance; }
set { SetAndRaisePropertyChanged(ref _balance, value); }
}
}
Here's the view code (MainWindow.xaml):
<Window x:Class="testProj.MainWindow"
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">
<Grid>
<Label Content="{Binding Balance}" />
</Grid>
</Window>
And the code behind (MainWindow.xaml.cs):
public partial class MainWindow
{
public MainWindow(AccountViewModel dataContext)
{
DataContext = dataContext;
InitializeComponent();
}
}
And for fun, the App.xaml.cs (set up for BuildAction - Page):
public partial class App
{
[STAThread]
public static void Main(string[] args)
{
new MainWindow(new AccountViewModel("123456789")).ShowDialog();
}
}
This will show you what you are expecting, and display the Balance correctly. There are a few things you can try to see what your issue is:
Is there any information in the output window when debugging that tells you if a binding error is occurring?
Can you give a shorter version of your application showing all parts (i.e. a short project that duplicates the issue) and upload it somewhere?
Delete the margin solved the problem. I guess the margin pushed out the label.
As the title says, im trying to display the hardcoded values I typed in my XAML file through databindings but in 1 method call instead of binding the textboxes in every specific property.
Model:
public class Person
{
public string Namn { get; set; }
public DateTime Födelsedatum { get; set; }
public int Betyg { get; set; }
public int AntalBarn { get; set; }
public int Favoritsiffra { get; set; }
public string Kommentar { get; set; }
}
View:
<Window x:Class="PiedPiper.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Inlämningsuppgift WPF" Height="400" Width="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Content="Personuppgifter" FontSize="35" Grid.Column="0" Grid.Row="0"></Label>
<Separator Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Margin="10,2,-50,2"/>
<Label Content="Namn:" Grid.Column="0" Grid.Row="2" Margin="5,5,210,0"></Label>
<TextBox Name="NamnTextBox" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="118,5,-50,0"/>
<Label Content="Födelsedatum:" Grid.Column="0" Grid.Row="3" Margin="5,5,210,0"></Label>
<TextBox Name="FödelsedatumTextBox" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Margin="118,5,-50,0"></TextBox>
<Label Content="Betyg" Grid.Column="0" Grid.Row="4" Margin="5,5,210,0"></Label>
<ComboBox Name="BetygComboBox" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Margin="218,5,-50,0"></ComboBox>
<Label Content="Antal barn (0-20):" Grid.Column="0" Grid.Row="5" Margin="5,5,210,0"></Label>
<Slider Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Margin="125,5,25,0"></Slider>
<TextBox Name="AntalBarnTextBox" Grid.Row="5" Grid.Column="0" Grid.ColumnSpan="2" Margin="300,5,-50,0"></TextBox>
<Label Content="Favoritsiffra (0-99):" Grid.Column="0" Grid.Row="6" Margin="5,5,210,0"></Label>
<TextBox Name="FavoritsiffraTextBox" Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" Margin="225,5,-50,0"></TextBox>
<CheckBox Content="Visa kommentar" Grid.Row="7" Grid.Column="0" Margin="5,5,210,0"></CheckBox>
<TextBox Name="KommentarTextBox" Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2" Margin="125,5,-50,-50" Grid.RowSpan="2"></TextBox>
<Button Content="Spara" Grid.Row="8" Grid.Column="0" Margin="125,55,-50,-80" Grid.ColumnSpan="2"></Button>
<Separator Grid.Row="9" Grid.Column="0" Grid.ColumnSpan="2" Margin="10,85,-50,-80"></Separator>
<Label Content="Andre Kordasti" Grid.Row="10" Grid.Column="0" Margin="10,85,-50,-80"></Label>
<Button Content="Avsluta" Grid.Row="10" Grid.Column="0" Margin="225,85,-50,-80" Grid.ColumnSpan="2"></Button>
</Grid>
ViewModel:
public class PersonViewModel : INotifyPropertyChanged
{
public PersonViewModel(Person person)
{
Namn = person.Namn;
Födelsedatum = person.Födelsedatum;
Betyg = person.Betyg;
AntalBarn = person.AntalBarn;
Favoritsiffra = person.Favoritsiffra;
Kommentar = person.Kommentar;
GetPerson();
}
private void GetPerson()
{
Namn = "KurtSune";
Födelsedatum = new DateTime(1980, 09, 06);
Betyg = 3;
AntalBarn = 7;
Favoritsiffra = 10;
Kommentar = "Kommentar...";
var mainWindow = new MainWindow();
mainWindow.NamnTextBox.Text = Namn;
mainWindow.FödelsedatumTextBox.Text = Convert.ToString(Födelsedatum);
mainWindow.BetygComboBox.SelectedValue = Betyg;
mainWindow.AntalBarnTextBox.Text = Convert.ToString(AntalBarn);
mainWindow.FavoritsiffraTextBox.Text = Convert.ToString(Favoritsiffra);
mainWindow.KommentarTextBox.Text = Kommentar;
}
public ICommand SaveCommand { get; set; }
public ICommand AbortCommand { get; set; }
public bool CanSave
{
get
{
if (Namn == null)
{
return false;
}
return !String.IsNullOrWhiteSpace(Namn);
}
}
private string _namn;
public string Namn
{
get { return _namn; }
set { _namn = value; OnPropertyChanged("Namn"); }
}
private DateTime _födelsedatum;
public DateTime Födelsedatum
{
get { return _födelsedatum; }
set { _födelsedatum = value; OnPropertyChanged("Födelsedatum"); }
}
private int _betyg;
public int Betyg
{
get { return _betyg; }
set { _betyg = value; OnPropertyChanged("Betyg"); }
}
private int _antalBarn;
public int AntalBarn
{
get { return _antalBarn; }
set { _antalBarn = value; OnPropertyChanged("AntalBarn"); }
}
private int _favoritSiffra;
public int Favoritsiffra
{
get { return _favoritSiffra; }
set { _favoritSiffra = value; OnPropertyChanged("Födelsedatum"); }
}
private string _kommentar;
public string Kommentar
{
get { return _kommentar; }
set { _kommentar = value; OnPropertyChanged("Kommentar"); }
}
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
And to add my App.xaml.cs file:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var person = new Person();
var personViewModel = new PersonViewModel(person);
var mainWindow = new MainWindow();
mainWindow.DataContext = personViewModel;
mainWindow.Show();
}
}
So I dont get the following values to be displayed... Please help, Iam new to this.
Here is a simplified example where,
I made a DataTemplate where controls are bound to the Person properties. Benefits are obvious because as you will see whichever end is modified, the other will know/show these changes.
(added a button and a MessageBox to further demonstrate this)
Code:
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
var person = new Person {Name = "name1", Address = "address1"};
person.PropertyChanged += person_PropertyChanged;
DataContext = person;
}
private void person_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
MessageBox.Show("data changed");
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
var dataContext = (Person) DataContext;
dataContext.Name = "newName";
dataContext.Address = "newAddress";
}
}
internal class Person : INotifyPropertyChanged
{
private string _address;
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged();
}
}
public string Address
{
get { return _address; }
set
{
_address = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpfApplication1="clr-namespace:WpfApplication1"
Title="MainWindow"
Width="525"
Height="350">
<Window.Resources>
<DataTemplate x:Key="PersonTemplate" DataType="wpfApplication1:Person">
<StackPanel>
<TextBlock Text="Name" />
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Address" />
<TextBox Text="{Binding Address, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<Button Click="ButtonBase_OnClick" Content="Modify" />
<ContentControl Content="{Binding}" ContentTemplate="{StaticResource PersonTemplate}" />
</StackPanel>
</Grid>
</Window>
Now as you can see there are no hard-coded values in XAML, the object I have passed to DataContext has sample values instead.