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
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 want to make an analog of viewing images like on Facebook.When you click on the image and it opens in the popup.The main problem is that the source with a link to the picture is another usercontrol from the collection.
So, I have for this usercontrol with dimming.
<Grid Background="#33000000" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid HorizontalAlignment="Center" VerticalAlignment="Center" Background="#FFFFFF" MinHeight="100" MinWidth="200">
<Grid.RowDefinitions>
<RowDefinition Height="30"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="35"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Background="#EEEEEE">
<Label Content="{Binding Title}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
<Grid Grid.Row="1" Margin="10,20">
<Image Source="{Binding ImageURI}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Grid>
<Grid Grid.Row="2" Background="#EEEEEE">
<Button Content="OK" Command="{Binding CloseCommand}" Width="80" Height="25" />
</Grid>
</Grid>
</Grid>
And also VM
public class PopupVM : ViewModelBase
{
private bool _isOpen;
public string ImageURI { get; set; }
public string Title { get; set; }
public bool IsOpen { get { return _isOpen; } set { _isOpen = value; RaisePropertyChanged("IsOpen"); } }
private void ClosePopup()
{
IsOpen = false;
}
public RelayCommand CloseCommand
{
get;
private set;
}
public PopupVM()
{
CloseCommand = new RelayCommand(() => ClosePopup());
}
}
Using a usercontrol on a page
<local:PopupUserControl Visibility="{Binding popupVM_Instance.IsOpen, Converter={StaticResource BoolToVis}}" DataContext="{Binding popupVM_Instance}"/>
Model for another usercontrol with image source. Collection, which is bind to a ItemsControl
public class DialogMessageModel : ConversationModel
{
public ImageAttach Image { get; set; }
public bool HasImage => Image != null;
public bool HasMessage => Message != null;
public RelayCommand OpenImageCommand
{
get;
private set;
}
private void OpenImage()
{
TestVM.popupVM_Instance.ImageURI = Image.ImageURI;
TestVM.popupVM_Instance.IsOpen = true;
}
}
VM of page have Instance of PopupVM for another controls can get acces to open it
public static PopupVM popupVM_Instance { get; set; }
public TestVM()
{
popupVM_Instance = new PopupVM();
}
Opening doesn`t work.Its visibile from start.And dont hide if I click button.Im using MVVMLight too.
This might be trivial question, but just can't find good solution to solve this. I do have a usercontrol(MyUserControl) that is duplicated in the mainview. One instance of usercontrol presents source object and one presents target object. Views are similar, but I need to know which is source and which is target in the usercontrol's viewmodel. MainView has SourcenContent and TargetContent.So question is that how I can separate SourcenContent and TargetContent in the usercontrol's viewmodel?
MainWindow
`
<DockPanel LastChildFill="True">
<Border Height="400" DockPanel.Dock="Top">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="400"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
</Grid.RowDefinitions>
<GroupBox Header="Source Database" Grid.Row="0" Grid.Column="0">
<StackPanel Orientation="Vertical" >
<ContentControl Content="{Binding SourcenContent}" Name="source" VerticalAlignment="Stretch"/>
</StackPanel>
</GroupBox>
<GroupBox Header="Target Database" Grid.Row="0" Grid.Column="1">
<StackPanel Orientation="Vertical">
<ContentControl Content="{Binding TargetContent}" Name="Target" VerticalAlignment="Stretch"/>
</StackPanel>
</GroupBox>
</Grid>
</Border>
...
</DockPanel>
`
MainViewModel
public class MainViewModel : ViewModelBase
{
private UserControl _sourcenContent;
private UserControl _targetContent;
private MyUserControl _sourcenContentUserControl;
private MyUserControl _targetContentUserControl;
public MainViewModel()
{
_sourcenContentUserControl = new MyUserControl();
_sourcenContent = _sourcenContentUserControl;
_targetContentUserControl = new MyUserControl();
_targetContent = _targetContentUserControl;
}
...
public UserControl SourcenContent
{
get { return _sourcenContent; }
set
{
if (_sourcenContent != value)
{
_sourcenContent = value;
RaisePropertyChanged("SourcenContent");
}
}
}
public UserControl TargetContent
{
get { return _targetContent; }
set
{
if (_targetContent != value)
{
_targetContent = value;
RaisePropertyChanged("TargetDatabaseConnectionContent");
}
ViewModelLocator
public class ViewModelLocator
{
public ViewModelLocator()
{
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
SimpleIoc.Default.Register<MainViewModel>();
SimpleIoc.Default.Register<MyUserControl>();
}
public MainViewModel MainViewModel
{
get
{
return ServiceLocator.Current.GetInstance<MainViewModel>();
}
}
public MyUserControlViewModel MyUserControlVM
{
get
{
return ServiceLocator.Current.GetInstance<MyUserControlViewModel>(Guid.NewGuid().ToString());
}
}
}
Usercontrol
<Grid Margin="8">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="75"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" >
... Textboxes and other input controls
enter code here
MyUserControlViewModel
public class MyUserControlViewModel : ViewModelBase
{
public MyUserControlViewModel(IService service, ...)
{
**/* How do I know which user control created this SourcenContent or TargetContent*/**
}
Yes xaml binding is/was confusing me and the answer to my question was very trivial. If I access viewModelLocator from code side and not try to do binding in XAML
MainViewModel:
public MainViewModel()
{
//Get access to the ViewModelLocator
ViewModelLocator viewModelLocator = System.Windows.Application.Current.Resources["Locator"] as ViewModelLocator;
_sourcenContentUserControl = new MyUserControl();
_sourcenContentUserControl.DataContext = viewModelLocator.MyUserControlSourceVM;
var dataContext = _sourcenContentUserControl.DataContext as MyUserControlViewModel ;
dataContext.SetSource;
_sourcenContent = _sourcenContentUserControl;
_targetContentUserControl = new MyUserControl();
_targetContentUserControl.DataContext = viewModelLocator.MyUserControlTargetVM;
dataContext = _targetContentUserControl.DataContext as MyUserControlViewModel ;
dataContext.SetTarget;
_targetContent = _targetContentUserControl;
}
ViewModelLocator
public MyUserControlViewModel MyUserControlSourceVM
{
get
{
return ServiceLocator.Current.GetInstance<MyUserControlViewModel>(Guid.NewGuid().ToString());
}
public MyUserControlViewModel MyUserControlTargetVM
{
get
{
return ServiceLocator.Current.GetInstance<MyUserControlViewModel>(Guid.NewGuid().ToString());
}
}
[I'm sorry for my bad English]
In my MainWindow I have some ContentControl and I set his Content to some view called SimpleView. If that SimpleView has ListBox bounded to Collection, The view is alive even if I remove the SimpleView.
All my viewmodels are Implementing INotifyPropertyChanged.
This is my code:
MainWindow.xaml
<Window x:Class="WpfMemory.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>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="Set view" Click="SetView" />
<Button Content="Clear view" Click="ClearView" />
</StackPanel>
<ContentControl x:Name="ViewContainer" Grid.Row="1" Margin="4" />
</Grid>
</Window>
In code behind
private void SetView(object sender, RoutedEventArgs e)
{
var simpleViewModel = new ViewModels.SimpleViewModel();
var simpleview = new Views.SimpleView() { DataContext = simpleViewModel };
ViewContainer.Content = simpleview;
}
private void ClearView(object sender, RoutedEventArgs e)
{
ViewContainer.Content = null;
System.GC.Collect();
}
SampleView.xaml
<UserControl x:Class="WpfMemory.Views.SimpleView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition MinWidth="60"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="SomeText" />
<TextBox Text="{Binding SomeText}" Grid.Column="1"/>
<TextBlock Text="Large data" Grid.Row="1"/>
<ListBox ItemsSource="{Binding LargeData,Mode=OneTime}"
Grid.Row="2" Grid.Column="1"
DisplayMemberPath="Name" />
</Grid>
</UserControl>
View models
public sealed class SimpleViewModel : ViewModelBase
{
public SimpleViewModel()
{
var items = Enumerable.Range(1, 10000)
.Select(x => new SimpleItem()
{
Id = x,
Name = "Item " + x
})
.ToArray();
LargeData = new ObservableCollection<SimpleItem>(items);
}
public string SomeText{ get; set; } = "yehudah";
ObservableCollection<SimpleItem> largeData;
public ObservableCollection<SimpleItem> LargeData
{
get { return largeData; }
set { SetProperty(ref largeData, value); }
}
}
public sealed class SimpleItem : ViewModelBase
{
public int Id { get; set; }
public string Name { get; set; }
}
I run the application and Click on SetView and then on ClearView.
Now, in vs diagnostic tools I click on "Take snapshot" And this is the result:
Thanks for any help.
I think you should reset simpleview.DataContext when content control is unloaded.
You can do
simpleview.Unloaded += (_, __) => simpleview.DataContext = null;
when SetView.
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.