I am implementing MVVM pattern for my application. I have my Views, ViewModel, Model and Commands and Converters being implemented. Now I am not able to pass my textboxes values from my datatemplate to my ViewModel through the command binding. I am able to click on the button to attempt the update process but its not able to pass the textboxes value. Are there something i need to change on my command class?
Here is my XAML:
<DataGrid AutoGenerateColumns="False" Grid.Row="2" Grid.ColumnSpan="4" Grid.RowSpan="3" x:Name="productionLineConfigDataGrid" Margin="70,0.2,70,0" ItemsSource="{Binding listAllProductionLineConfigs}">
<DataTemplate>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="12" Text="ID: " VerticalAlignment="Center" />
<TextBlock x:Name="txtBlockLineId" FontSize="16" Foreground="MidnightBlue" Text="{Binding ProductionLineId, Mode=TwoWay}" VerticalAlignment="Center" />
</StackPanel>
<StackPanel>
<Button x:Name="btnUpdate" Content="Update" VerticalAlignment="Center" HorizontalAlignment="Right" Click="btnUpdate_Click" Command="{Binding DataContext.updateProductionLineConfigCommand, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:production_line_config_home}}}" CommandParameter="{Binding ProductionLineConfig}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</DataGrid>
Here is the method from my ViewModel:
public ProductionLineConfig ProductionLineConfig
{
get { return productionlineconfig; }
set
{
productionlineconfig = value;
OnPropertyChanged("ProductionLineConfig");
}
}
This is the error message i am getting:
System.Windows.Data Error: 40 : BindingExpression path error: 'ProductionLineConfig' property not found on 'object' ''ProductionLineConfig' (HashCode=47309994)'. BindingExpression:Path=ProductionLineConfig; DataItem='ProductionLineConfig' (HashCode=47309994); target element is 'Button' (Name=''); target property is 'CommandParameter' (type 'Object')
I have included the image for my application here
This is the entire xaml code here and this is the entire viewmodel code here
I'm only going to take a guess at this.
Assuming ProductionLineConfig is what you are binding to for ProductionLineId in the template. Then you probably are just wanting to pass your binding source as the command parameter
CommandParameter="{Binding}"
When {Binding} is empty, it means the Binding is bound to whatever Source there is. Which is also just shorthand for...
{Binding DataContext,RelativeSource={RelativeSource Self}}.
In turn (if the stars align), then it should be your ProductionLineConfig
Based on your code source, I made a sample implementation, to achieve your requirement.
Sample VM:
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApp5.ViewModels
{
public class ProductionLineConfigViewModel : INotifyPropertyChanged
{
public CustomCommand<ProductionLineConfig> UpdateCommand { get; }
public ProductionLineConfigViewModel()
{
PopulateProductionLineConfigs();
UpdateCommand = new CustomCommand<ProductionLineConfig>(UpdateConfig, (u) => true);
}
private ObservableCollection<ProductionLineConfig> _listAllProductionLineConfigs;
public ObservableCollection<ProductionLineConfig> listAllProductionLineConfigs
{
get { return _listAllProductionLineConfigs; }
set
{
_listAllProductionLineConfigs = value;
OnPropertyChanged();
}
}
// Call this from constructor.
private void PopulateProductionLineConfigs()
{
listAllProductionLineConfigs = new ObservableCollection<ProductionLineConfig>
{
new ProductionLineConfig
{
ProductionLineId = 1,
ProductionLineCode = "001",
ProductionLineCreatedDate = DateTime.Today.Date,
ProductionLineName = "safdsf",
ProductionLineStatus = true
},
new ProductionLineConfig
{
ProductionLineId = 1,
ProductionLineCode = "002",
ProductionLineCreatedDate = DateTime.Today.Date,
ProductionLineName = "sadfadfsdf",
ProductionLineStatus = true
}
};
}
private void UpdateConfig(ProductionLineConfig config)
{
MessageBox.Show("Line Name update: " + config.ProductionLineName);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class ProductionLineConfig
{
public int ProductionLineId { get; set; }
public string ProductionLineCode { get; set; }
public string ProductionLineName { get; set; }
public bool ProductionLineStatus { get; set; }
public DateTime ProductionLineCreatedDate { get; set; }
}
}
Sample XAML:
<Window x:Name="Root" x:Class="WpfApp5.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:viewModels="clr-namespace:WpfApp5.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<viewModels:ProductionLineConfigViewModel/>
</Window.DataContext>
<Grid Background="#FF006E8C">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Label Grid.ColumnSpan="4" Content="KAD ShopFloor System" HorizontalAlignment="Center" Margin="10" FontWeight="Bold" FontSize="30" FontFamily="Segoe UI" Foreground="White"/>
<Separator Grid.ColumnSpan="4" Grid.RowSpan="3" Background="White" Margin="0,-35,-0.4,39.2"/>
<DataGrid AutoGenerateColumns="False" Grid.Row="2" Grid.ColumnSpan="4" Grid.RowSpan="3" x:Name="productionLineConfigDataGrid" Margin="70,0.2,70,0"
ItemsSource="{Binding DataContext.listAllProductionLineConfigs, ElementName=Root}">
<DataGrid.Columns>
<DataGridTextColumn Header="ID" Binding="{Binding ProductionLineId, Mode=TwoWay}"/>
<DataGridTextColumn Header="Production Line Code" Binding="{Binding ProductionLineCode, Mode=TwoWay}"/>
<DataGridTextColumn Header="Production Line Name" Binding="{Binding ProductionLineName, Mode=TwoWay}"/>
<DataGridTextColumn Header="Status" Binding="{Binding ProductionLineStatus, Mode=TwoWay}"/>
<DataGridTextColumn Header="Created Date" Binding="{Binding ProductionLineCreatedDate, Mode=TwoWay}"/>
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<Border BorderThickness="0" Background="BlanchedAlmond" Padding="10">
<StackPanel Orientation="Vertical" x:Name="stck">
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="12" Text="ID: " VerticalAlignment="Center" />
<TextBlock x:Name="txtBlockLineId" FontSize="16" Foreground="MidnightBlue" Text="{Binding ProductionLineId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="12" Text="Line Code: " VerticalAlignment="Center" />
<TextBlock x:Name="txtBlockLineCode" FontSize="16" Foreground="MidnightBlue" Text="{Binding ProductionLineCode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="12" Text="Line Name: " VerticalAlignment="Center" />
<TextBox x:Name="txtLineName" FontSize="16" Foreground="MidnightBlue" Text="{Binding ProductionLineName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Center" />
</StackPanel>
<!--<StackPanel Orientation="Horizontal">
<TextBlock FontSize="12" Text="Status: " VerticalAlignment="Center" />
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type DataGrid}},
Path=DataContext.Statusstring}" SelectedValue="{Binding ProductionLineStatus, Converter={StaticResource statusToBooleanConverter}, Mode=TwoWay}" x:Name="cbProductionLineStatus" FlowDirection="LeftToRight" FontSize="16" Foreground="MidnightBlue"
HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
</StackPanel>-->
<StackPanel>
<Button x:Name="btnUpdate" Content="Update" VerticalAlignment="Center" HorizontalAlignment="Right"
Command="{Binding DataContext.UpdateCommand, ElementName=Root}"
CommandParameter="{Binding}" />
</StackPanel>
</StackPanel>
</Border>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
</Grid>
</Window>
Sample output:
Key changes here,
You need to change your list to observable collection
Create a custom command that accepts an object, see this post: [UWP/MVVM]Enable/Disable Button in RadDataGrid Data Template Column that have commands bound to them upon conditions
Command Parameter should be the iterated item, can be achieve by CommandParameter={Binding}
In you two way bindings, make sure to add UpdateSourceTrigger=PropertyChanged
Related
I am using a UserControl within a UserControl. Everything works except one piece. I get:
BindingExpression path error: 'SelectedItem' property not found on 'object' ''OrganizationDetailsVM' (HashCode=21529561)'. BindingExpression:Path=SelectedItem; DataItem='OrganizationDetailsVM' (HashCode=21529561); target element is 'ExtendedTreeView' (Name='tvApplications'); target property is 'SelectedItem_' (type 'Object')
Here are the relevant bits:
OrganizationDetailsVM.cs
private AdoptionApplication selectedApplication;
public AdoptionApplication SelectedApplication
{
get { return this.selectedApplication; }
set
{
if (this.selectedApplication != value)
{
this.selectedApplication = value;
RaisePropertyChanged("SelectedApplication");
}
}
}
private object selectedTreeItem;
public object SelectedTreeItem
{
get { return this.selectedTreeItem; }
set
{
if (this.selectedTreeItem != value)
{
this.selectedTreeItem = value;
SelectedApplication = selectedTreeItem as AdoptionApplication;
RaisePropertyChanged("SelectedTreeItem");
RaisePropertyChanged("SelectedApplication");
}
}
}
// other stuff
OrganizationDetails.xaml
<uc:ApplicationsControl x:Name="ucApplications" SelectedItem="{Binding SelectedTreeItem}"
AddApplicationCommand="{Binding AddApplicationCommand}"
EditApplicationCommand="{Binding EditApplicationCommand}"
DeleteApplicationCommand="{Binding DeleteApplicationCommand}"/>
ApplicationsControl.xaml
<UserControl x:Class="CareerChanges.Views.UserControls.ApplicationsControl"
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:cc="clr-namespace:CareerChanges"
xmlns:local="clr-namespace:CareerChanges.Views.UserControls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<cc:SimpleFolderConverter x:Key="folderConverter"/>
</UserControl.Resources>
<Grid Background="#FFE5E5E5"
Loaded="Grid_Loaded">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="419*"/>
<ColumnDefinition Width="75*"/>
</Grid.ColumnDefinitions>
<local:ExtendedTreeView x:Name="tvApplications"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
SelectedItem_="{Binding Path=SelectedItem, Mode=TwoWay, ValidatesOnNotifyDataErrors=True}"
ItemsSource="{Binding AdoptionApplications}">
<local:ExtendedTreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type cc:AdoptionApplication}">
<HierarchicalDataTemplate.ItemsSource>
<MultiBinding Converter="{StaticResource folderConverter}" ConverterParameter=", Breeds, FamilyMembers, Persons">
<Binding Path="Organization"/>
<Binding Path="ApplicationPreferences"/>
<Binding Path="FamilyMembers"/>
<Binding Path="ApplicationPersons"/>
</MultiBinding>
</HierarchicalDataTemplate.ItemsSource>
<StackPanel Orientation="Horizontal" Background="Aquamarine">
<TextBlock Text="Application (" />
<TextBlock Text="{Binding Path=ApplicationID}"/>
<TextBlock Text=") "/>
<TextBlock Text="{Binding Path=Title}" FontStyle="Italic"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding Path=ApplicationDate, StringFormat=dd-MMM-yyyy}"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding ApplicationStatusID, Converter={StaticResource ApplicationStatusConverter}}"/>
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type cc:ApplicationPreference}" ItemsSource="{Binding Path=Colours}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding BreedID, Converter={StaticResource BreedConverter}}" />
<TextBlock Text=" [" Foreground="Blue" />
<TextBlock Text="{Binding Colours.Count}" Foreground="Blue" />
<TextBlock Text="]" Foreground="Blue" />
</StackPanel>
</HierarchicalDataTemplate>
<HierarchicalDataTemplate DataType="{x:Type cc:FolderItem}"
ItemsSource="{Binding Path=Items}">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="txtName" Text="{Binding Path=Name}" />
<TextBlock Text=" [" Foreground="Blue" />
<TextBlock Text="{Binding Items.Count}" Foreground="Blue" />
<TextBlock Text="]" Foreground="Blue" />
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type cc:Person}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Given}" />
<TextBlock Text=" " />
<TextBlock Text="{Binding Path=Surname}" />
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type cc:FamilyMemberType}">
<TextBlock Text="{Binding Path=FamilyMemberTypeID, Converter={StaticResource FamilyTypeConverter}}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type cc:ColourPreference}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" Orientation="Horizontal">
<TextBlock Text="{Binding ColourID, Converter={StaticResource ColourConverter}}" />
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Horizontal">
<TextBlock Text=" Male: "/>
<CheckBox IsChecked="{Binding Male}" IsEnabled="False"/>
<TextBlock Text=" Female: "/>
<CheckBox IsChecked="{Binding Female}" IsEnabled="False"/>
</StackPanel>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type cc:Organization}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Organization: " />
<TextBlock Text="{Binding Path=OrganizationName}" />
</StackPanel>
</DataTemplate>
</local:ExtendedTreeView.Resources>
</local:ExtendedTreeView>
<StackPanel Grid.Column="1">
<Button Name="btnAddApplication" Margin="2" Command="{Binding Path=AddApplicationCommand}">Add</Button>
<Button Name="btnEditApplication" Margin="2" Command="{Binding Path=EditApplicationCommand}">Edit</Button>
<Button Name="btnDeleteApplication" Margin="2" Command="{Binding Path=DeleteApplicationCommand}">Delete</Button>
</StackPanel>
</Grid>
</UserControl>
ApplicationsControl.xaml.cs
public object SelectedItem
{
get { return GetValue(SelectedItemProperty); }
set { SetValue(SelectedItemProperty, value); }
}
public static readonly DependencyProperty SelectedItemProperty =
DependencyProperty.Register(
"SelectedItem",
typeof(object),
typeof(ApplicationsControl),
new FrameworkPropertyMetadata(
null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
Finally, this is the code for the ExtendedTreeView from this question: Data binding to SelectedItem in a WPF Treeview
ExtendedTreeView.cs
public class ExtendedTreeView : TreeView
{
public ExtendedTreeView()
: base()
{
this.SelectedItemChanged += new RoutedPropertyChangedEventHandler<object>(___ICH);
}
void ___ICH(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (SelectedItem != null)
{
SetValue(SelectedItem_Property, SelectedItem);
}
}
public object SelectedItem_
{
get { return (object)GetValue(SelectedItem_Property); }
set { SetValue(SelectedItem_Property, value); }
}
public static readonly DependencyProperty SelectedItem_Property = DependencyProperty.Register("SelectedItem_", typeof(object), typeof(ExtendedTreeView), new UIPropertyMetadata(null));
}
For some reason, the binding isn't making it from OrganizationDetailsVM, through OrganizationDetails.xaml to ApplicationsControl.xaml and down to ExtendedTreeview. All the other bindings work perfectly. The tree view does what it should, except for binding to the selected item.
To sum up, I'm having a problem getting from here:
<uc:ApplicationsControl x:Name="ucApplications" SelectedItem="{Binding SelectedTreeItem}"
AddApplicationCommand="{Binding AddApplicationCommand}"
EditApplicationCommand="{Binding EditApplicationCommand}"
DeleteApplicationCommand="{Binding DeleteApplicationCommand}"/>
... to here:
<local:ExtendedTreeView x:Name="tvApplications"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
SelectedItem_="{Binding Path=SelectedItem, Mode=TwoWay, ValidatesOnNotifyDataErrors=True}"
ItemsSource="{Binding AdoptionApplications}">
It boils down to this binding not working:
SelectedItem_="{Binding Path=SelectedItem, Mode=TwoWay, ValidatesOnNotifyDataErrors=True}"
Found it! I'm still new at data binding. After looking at some other code, I modified the binding to:
SelectedItem="{Binding Path=SelectedItem, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:DogPersonsControl}}}">
Ok, I've been practicing with MVVM Pattern on UWP, now I've created an object Customer EditableCustomer which is my helper property between my view and my model at my VM.
if my four properties FirstName, LastName, Email and Phone arent nulls or emptys my savecommand button should enable.
but I havent been able to raise my property changes on my EditableCustomer so that way I could Raise my SaveCommand.RaiseCanExecuteChanged().
this is my code for my view:
<UserControl
x:Class="MVVMHeirarchiesDemo.Views.AddEditCustomerView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MVVMHeirarchiesDemo.Views"
xmlns:viewmodel="using:MVVMHeirarchiesDemo.ViewModel"
xmlns:conv="using:MVVMHeirarchiesDemo.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<UserControl.DataContext>
<viewmodel:AddEditCustomerViewModel/>
</UserControl.DataContext>
<UserControl.Resources>
<conv:ValidationMessageConverter x:Key="ValidationMessageConverter"/>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid x:Name="grid1"
HorizontalAlignment="Left"
Margin="10,10,0,0"
VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="First Name:"
Grid.Column="0"
Grid.Row="0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3"/>
<StackPanel Orientation="Vertical"
Grid.Column="1"
Grid.Row="0">
<TextBox x:Name="firstNameTextBox"
Text="{Binding EditableCustomer.FirstName, Mode=TwoWay}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3"
Height="23"
Width="120"/>
<TextBlock x:Name="firstNameErrorMessage"
Text="{Binding EditableCustomer.ValidationMessages[FirstName], Converter={StaticResource ValidationMessageConverter}}"
Width="{Binding Width, ElementName=firstNameTextBox, Mode=OneWay}"
TextWrapping="Wrap"/>
</StackPanel>
<TextBlock Text="Last Name:"
Grid.Column="0"
Grid.Row="1"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3"/>
<StackPanel Orientation="Vertical"
Grid.Column="1"
Grid.Row="1">
<TextBox x:Name="lastNameTextBox"
Text="{Binding EditableCustomer.LastName, Mode=TwoWay}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Width="{Binding Width, ElementName=firstNameTextBox, Mode=OneWay}"
Height="{Binding Height, ElementName=firstNameTextBox, Mode=OneWay}"
Margin="3"/>
<TextBlock x:Name="lastNameErrorMessage"
Text="{Binding EditableCustomer.ValidationMessages[LastName], Converter={StaticResource ValidationMessageConverter}}"
Width="{Binding Width, ElementName=firstNameTextBox, Mode=OneWay}"
TextWrapping="Wrap"/>
</StackPanel>
<TextBlock Text="Email:"
Grid.Column="0"
Grid.Row="2"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3"/>
<StackPanel Orientation="Vertical"
Grid.Column="1"
Grid.Row="2">
<TextBox x:Name="emailTextBox"
Text="{Binding EditableCustomer.Email, Mode=TwoWay}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Width="{Binding Width, ElementName=firstNameTextBox, Mode=OneWay}"
Height="{Binding Height, ElementName=firstNameTextBox, Mode=OneWay}"
Margin="3"/>
<TextBlock x:Name="emailErrorMessage"
Text="{Binding EditableCustomer.ValidationMessages[Email], Converter={StaticResource ValidationMessageConverter}}"
Width="{Binding Width, ElementName=firstNameTextBox, Mode=OneWay}"
TextWrapping="Wrap"/>
</StackPanel>
<TextBlock Text="Phone:"
Grid.Column="0"
Grid.Row="3"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3"/>
<StackPanel Orientation="Vertical"
Grid.Column="1"
Grid.Row="3">
<TextBox x:Name="phoneTextBox"
Text="{Binding EditableCustomer.Phone, Mode=TwoWay}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Width="{Binding Width, ElementName=firstNameTextBox, Mode=OneWay}"
Height="{Binding Height, ElementName=firstNameTextBox, Mode=OneWay}"
Margin="3"/>
<TextBlock x:Name="phoneErrorMessage"
Text="{Binding EditableCustomer.ValidationMessages[Phone], Converter={StaticResource ValidationMessageConverter}}"
Width="{Binding Width, ElementName=firstNameTextBox, Mode=OneWay}"
TextWrapping="Wrap"/>
</StackPanel>
</Grid>
<Grid Grid.Row="1">
<StackPanel Orientation="Horizontal">
<Button x:Name="saveCommandButton"
Content="Save"
Command="{Binding SaveCommand}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="25,5,0,0"
Width="75"/>
<Button x:Name="addCommandButton"
Content="Add"
Command="{Binding SaveCommand}"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Margin="25,5,0,0"
Width="{Binding Width, ElementName=saveCommandButton, Mode=OneWay}"/>
<Button x:Name="cancelCommandButton"
Content="Cancel"
Command="{Binding CancelCommand}"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="25,5,0,0"
Width="{Binding Width, ElementName=saveCommandButton, Mode=OneWay}"/>
</StackPanel>
</Grid>
</Grid>
As you can see at my textboxes the text property are bind to my EditableCustomer.(propertyName)
my ViewModel is this one:
public class AddEditCustomerViewModel : BindableBase
{
public AddEditCustomerViewModel()
{
CancelCommand = new MyCommand(OnCancel);
SaveCommand = new MyCommand(OnSave, CanSave);
EditableCustomer = new Customer();
}
private Customer _editableCustomer;
public Customer EditableCustomer
{
get
{
return _editableCustomer;
}
set
{
SetProperty(ref _editableCustomer, value);
SaveCommand.RaiseCanExecuteChanged();
}
}
public MyCommand CancelCommand { get; private set; }
public MyCommand SaveCommand { get; private set; }
public event Action Done = delegate { };
private void OnCancel()
{
Done();
}
private void OnSave()
{
Done();
}
private bool CanSave()
{
if (HasEmptyFields())
return false;
return true;
}
private bool HasEmptyFields()
{
return string.IsNullOrEmpty(EditableCustomer.FirstName) || string.IsNullOrEmpty(EditableCustomer.LastName)
|| string.IsNullOrEmpty(EditableCustomer.Phone) || string.IsNullOrEmpty(EditableCustomer.Email);
}
}
All i need to do is to be able to trigger the setter for my EditableCustomer property, but I've been able to do that with it.
what am i missing here??
I see my code and I think that it should be raise everytime each field has text until it becomes true once all my text boxes have text on them.
but nothing happens.
just to understand this is my BindableBase is a class where I apply my INotifyPropertyChanged.
public class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void SetProperty<T>(ref T member, T val, [CallerMemberName]string propertyName = null)
{
if (object.Equals(member, val))
return;
member = val;
OnPropertyChanged(propertyName);
}
}
I hope someone can enlight me, meanwhile i'll try to keep digging on this issues.
It looks like SaveCommand.RaiseCanExecuteChanged(); is only called when you set the EditableCustomer, but when you modify the values inside that object (ie name, email, etc), nothing is notifying the UI that SaveCommand can execute.
If you're going to break up your view models like that, where you have an encapsulated class that manages the input, then it will need to notify the parent view model of changes. Your AddEditCustomerViewModel does not know when the properties have changed and cannot RaiseCanExecuteChanged() accordingly.
Update
In your case, I would recommend having the view-model route all properties that can be modified by the UI form. For example:
public class AddEditCustomerViewModel : BindableBase
{
public string FirstName
{
get { return _editableCustomer.FirstName; }
set
{
_editableCustomer.FirstName = value;
OnPropertyChanged(nameof(FirstName));
if (!HasEmptyFields())
{
SaveCommand.RaiseCanExecuteChanged();
}
}
}
}
and your XAML should bind directly to the view-model, not the model:
<TextBox Text="{Binding FirstName, Mode=TwoWay}" />
I am just starting off with windows 8 apps development and I want to be able to filter a gridview using the selected value in a combobox
my xaml page code behind
public sealed partial class MainPage : Page
{
public ObservableCollection<Recording> MyMusic = new ObservableCollection<Recording>();
public MainPage()
{
this.InitializeComponent();
// Add items to the collection.
MyMusic.Add(new Recording("Chris Sells", "Chris Sells Live",
new DateTime(2008, 2, 5)));
MyMusic.Add(new Recording("Luka Abrus",
"The Road to Redmond", new DateTime(2007, 4, 3)));
MyMusic.Add(new Recording("Jim Hance",
"The Best of Jim Hance", new DateTime(2007, 2, 6)));
// Set the data context for the combo box.
//ComboBox1.DataContext = MyMusic;
this.DataContext = new CollectionViewSource { Source = MyMusic };
}
}
Class
public class Recording
{
public Recording() { }
public Recording(string artistName, string cdName, DateTime release)
{
Artist = artistName;
Name = cdName;
ReleaseDate = release;
}
public string Artist { get; set; }
public string Name { get; set; }
public DateTime ReleaseDate { get; set; }
// Override the ToString method.
public override string ToString()
{
return Name + " by " + Artist + ", Released: " + ReleaseDate.ToString("d");
}
}
Xaml mark up
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ComboBox x:Name="ComboBox1" HorizontalAlignment="Left" Margin="542,108,0,0" VerticalAlignment="Top" Width="360" ItemsSource="{Binding}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock Text="Artist:" Margin="2" />
<TextBlock Text="{Binding Artist}" Margin="2" />
<TextBlock Text="CD:" Margin="10,2,0,2" />
<TextBlock Text="{Binding Name}" Margin="2" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!--<StackPanel x:Name="RecordingDetails" Margin="542,150,10,30">
<TextBlock Text="{Binding Artist}" FontWeight="Bold" FontSize="30" />
<TextBlock Text="{Binding Name}" FontStyle="Italic" FontSize="30" />
<TextBlock Text="{Binding ReleaseDate}" FontSize="30" />
</StackPanel>-->
<GridView
x:Name="itemGridView"
AutomationProperties.AutomationId="ItemGridView"
AutomationProperties.Name="Grouped Items"
Grid.RowSpan="2"
Padding="116,137,40,46"
ItemsSource="{Binding}"
SelectionMode="None"
IsSwipeEnabled="false" >
<GridView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" Width="250" Height="250">
<StackPanel VerticalAlignment="Bottom" Background="{ThemeResource ListViewItemOverlayBackgroundThemeBrush}" Name="test">
<TextBlock Text="{Binding Artist}" Foreground="{ThemeResource ListViewItemOverlayForegroundThemeBrush}" Style="{StaticResource TitleTextBlockStyle}" Height="60" Margin="15,0,15,0"/>
<TextBlock Text="{Binding Name}" Foreground="{ThemeResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
<TextBlock Text="{Binding ReleaseDate}" Foreground="{ThemeResource ListViewItemOverlaySecondaryForegroundThemeBrush}" Style="{StaticResource CaptionTextBlockStyle}" TextWrapping="NoWrap" Margin="15,0,15,10"/>
</StackPanel>
</Grid>
</DataTemplate>
</GridView.ItemTemplate>
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid GroupPadding="0,0,70,0"/>
</ItemsPanelTemplate>
</GridView.ItemsPanel>
so essentially I just need to filter the gridview dynamically using combo box value selected.
Kindly appreciate help and guidaince please.
This is a msdn example.
You can refer this msdn page link for further details.
UserControl
<UserControl x:Class="TestDataBindingQS.Page2"
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="using:TestDataBindingQS"
mc:Ignorable="d"
d:DesignHeight="768" d:DesignWidth="1366">
<UserControl.Resources>
<local:StringFormatter x:Key="StringConverter"/>
</UserControl.Resources>
<Grid x:Name="LayoutRoot" Background="#FF0C0C0C">
<StackPanel Width="750" Height="200"
VerticalAlignment="Center" HorizontalAlignment="Center">
<ComboBox x:Name="ComboBox1" ItemsSource="{Binding}"
Foreground="Black" FontSize="30" Height="50" Width="750">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="2">
<TextBlock Text="Artist:" Margin="2" />
<TextBlock Text="{Binding Artist}" Margin="2" />
<TextBlock Text="CD:" Margin="10,2,0,2" />
<TextBlock Text="{Binding Name}" Margin="2" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<!--The UI for the details view-->
<StackPanel x:Name="RecordingDetails">
<TextBlock Text="{Binding Artist}" FontSize="30" FontWeight="Bold" />
<TextBlock Text="{Binding Name}" FontSize="30" FontStyle="Italic" />
<TextBlock Text="{Binding ReleaseDate,
Converter={StaticResource StringConverter},
ConverterParameter=Released: \{0:d\}}" FontSize="30" />
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
converter class
public class StringFormatter : IValueConverter
{
// This converts the value object to the string to display.
// This will work with most simple types.
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
// Retrieve the format string and use it to format the value.
string formatString = parameter as string;
if (!string.IsNullOrEmpty(formatString))
{
return string.Format(culture, formatString, value);
}
// If the format string is null or empty, simply
// call ToString() on the value.
return value.ToString();
}
// No need to implement converting back on a one-way binding
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
And this produces the following output
I have a Pivot view in my WP8 app, and I'm trying to access the data collection from the view's class upon Click event "removePin".
The source of the data collection is another class called Pins.
how can I achieve that?
this is my snippet code for the XAML for that particular part
<phone:PivotItem Header="Pins">
<!-- Content Panel -->
<Grid x:Name="ContentPanel2" HorizontalAlignment="Left" Height="583" Margin="10,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="460">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="400*"/>
<ColumnDefinition Width="0*"/>
<ColumnDefinition Width="87*"/>
</Grid.ColumnDefinitions>
<ListBox x:Name="lstData2" ItemsSource="{Binding DataCollection2, Source={StaticResource PinsCollection}}"
Grid.ColumnSpan="3" Foreground="#FF1D53D0" Height="583" VerticalAlignment="Bottom">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Tap="StackPanel_Tap">
<Image Margin="8" VerticalAlignment="Top" Source="{Binding ImageUri}"
Width="100" Height="100" />
<StackPanel Height="93" Width="259" >
<TextBlock Margin="8" Width="250" TextWrapping="Wrap" VerticalAlignment="Top" HorizontalAlignment="Left" Foreground="#FF1D53D0"
Text="{Binding Pinnedname}" Height="33" RenderTransformOrigin="0.5,0.5" FontFamily="Segoe WP SemiLight" FontSize="24" FontWeight="Bold" />
<TextBlock Width="155" Margin="8,0,8,8" VerticalAlignment="Top"
HorizontalAlignment="Left" Text="{Binding Status}" Foreground="#FF1D53D0" FontFamily="Segoe WP SemiLight" />
</StackPanel>
<toolkit:ContextMenuService.ContextMenu>
<toolkit:ContextMenu>
<toolkit:MenuItem Header="Remove Pin" Click="RemovePin_Click" Tag="{Binding pinId}"/>
</toolkit:ContextMenu>
</toolkit:ContextMenuService.ContextMenu>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<!-- End of Content Panel -->
</Grid>
Create a custom command class:
public class CustomCommand : ICommand
{
Action _exec;
public CustomCommand(Action exec)
{
_exec = exec;
}
public void Execute(object parameter)
{
if (_exec != null) _exec();
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
Add it to the view model for elements of pins collection (I supposed it's PinItem)
public CustomCommand RemovePinCommand
{
get { return (CustomCommand)GetValue(RemovePinCommandProperty); }
set { SetValue(RemovePinCommandProperty, value); }
}
public static readonly DependencyProperty RemovePinCommandProperty =
DependencyProperty.Register("RemovePinCommand",
typeof(CustomCommand),
typeof(PinItem),
new UIPropertyMetadata(null));
In the constructor of that class implement your logic for this command:
RemovePinCommand = new CustomCommand(() =>
{
//delete this pin from its parent collection
});
final step is to bind the command to the menu item:
<toolkit:MenuItem Header="Remove Pin" Command="{Binding RemovePinCommand}"/>
Here is my problem i need to solve.
My data content is in DemoList class:
NOTICE: DemoHeader object contains a ObservableCollection of DemoItem objects, and DemoList object contains ObservableCollection of DemoHeader objects
public enum Type
{
article,
product,
material
}
public class DemoHeader
{
private ObservableCollection<DemoItem> _items;
public ObservableCollection<DemoItem> Items
{
get { return _items; }
set { _items = value; }
}
public DemoHeader(string document)
{
this._salesOrder = document;
_items = new ObservableCollection<DemoItem>();
}
private string _salesOrder;
public string SalesOrder
{
get { return _salesOrder; }
set { _salesOrder = value; }
}
}
public class DemoItem
{
public DemoItem(string name, Type type)
{
this._name = name;
this._type = type;
}
private Type _type;
public Type Type
{
get { return _type; }
set { _type = value; }
}
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
public class DemoList : ObservableCollection<DemoHeader>//, ICollectionView
{
public DemoList()
{
DemoHeader dd = new DemoHeader("Doc-1");
dd.Items.Add(new DemoItem("T-1", Type.article));
dd.Items.Add(new DemoItem("M-1", Type.material));
DemoHeader dd2 = new DemoHeader("Doc-2");
dd2.Items.Add(new DemoItem("P-1", Type.product));
dd2.Items.Add(new DemoItem("P-2", Type.product));
this.Add(dd);
this.Add(dd2);
}
}
XAML:
NOTICE: I have 4 CollectionViewSource for each ListBox.
<Window x:Class="WuZet.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:c="clr-namespace:WuZet"
Title="WuZet" WindowStartupLocation="CenterScreen" ResizeMode="CanResize" Loaded="window_loaded" Background="#ECE9D8" WindowStyle="ToolWindow" Icon="/WuZet;component/Images/ksi_ikona.ico" Topmost="True" WindowState="Maximized" SizeToContent="WidthAndHeight">
<Window.Resources>
<CollectionViewSource x:Key="list" Source="{Binding}"></CollectionViewSource>
<CollectionViewSource x:Key="wares" Source="{Binding Source={StaticResource list}, Path=Items}" Filter="wareFilter"></CollectionViewSource>
<CollectionViewSource x:Key="materials" Source="{Binding Source={StaticResource list}, Path=Items}" Filter="materialFilter"></CollectionViewSource>
<CollectionViewSource x:Key="products" Source="{Binding Source={StaticResource list}, Path=Items}" Filter="productFilter"></CollectionViewSource>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="80"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="200"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition Height="200"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="0" Grid.ColumnSpan="2" Margin="5,5,5,5">
<TextBox/>
<Button Content="ok" Margin="0,5,0,0" HorizontalAlignment="Stretch" Height="30" Width="150" Click="Button_Click"/>
</StackPanel>
<StackPanel Grid.RowSpan="2" Grid.Column="2">
<ListBox Name="orders" IsEnabled="{Binding ElementName=check, Path=IsChecked}" Margin="85,5,85,5" Height="70" ItemsSource="{Binding Source={StaticResource list}}" DisplayMemberPath="SalesOrder"/>
<CheckBox Name="check" HorizontalAlignment="Center" Content="Wybierz zamówienie" IsChecked="False"/>
</StackPanel>
<GroupBox Header="Wares" Grid.Row="2" Grid.Column="0">
<ListBox Name="lbWares" ItemsSource="{Binding Source={StaticResource wares}}" >
<ListBox.ItemTemplate>
<DataTemplate>
<!--<StackPanel Orientation="Horizontal">-->
<TextBlock Text="{Binding Path=Name}"></TextBlock>
<!--<TextBlock Text="{Binding ZaE_TwrKod}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding ZaE_Ilosc}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding ZaE_TwrNazwa}" />-->
<!--</StackPanel>-->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
<GroupBox Header="Materials" Grid.Row="2" Grid.Column="1">
<ListBox Name="lbMaterials" ItemsSource="{Binding Source={StaticResource materials}}">
<ListBox.ItemTemplate>
<DataTemplate>
<!--<StackPanel Orientation="Horizontal">-->
<TextBlock Text="{Binding Path=Name}"/>
<!--<TextBlock Text=", " />
<TextBlock Text="{Binding ZaE_Ilosc}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding ZaE_TwrNazwa}" />-->
<!--</StackPanel>-->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
<GroupBox Header="Products" Grid.Row="2" Grid.Column="2">
<ListBox Name="lbProducts" ItemsSource="{Binding Source={StaticResource products}}">
<ListBox.ItemTemplate>
<DataTemplate>
<!--<StackPanel Orientation="Horizontal">-->
<TextBlock Text="{Binding Path=Name}"/>
<!--<TextBlock Text=", " />
<TextBlock Text="{Binding ZaE_Ilosc}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding ZaE_TwrNazwa}" />
</StackPanel>-->
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</GroupBox>
</Window>
DemoList object is binding to CollectionViewList x:Key=list.
Here is my buisiness logic i need to implement:
If checkbox is marked i need to return selected ListBoxItem to the
corresponding containers [wares, products, materials] - this logic is
working
If checkbox is unmarked i need to return ALL items
[ObservableCollection] of ALL headers to the corresponding
containers [wares, products, materials]
I'm stuck right here, can anyone suggest me a solution?
--- 2013-11-04 20:38
Sry for misunderstanding, and for my bad english.
I uploaded some screen to be more clear.
http://imgur.com/UowQrRP
As you see on the screen i need to implement behavior for checkbox.
When it is unchecked each DemoItem object must be display in one of 3 containers.
Each container is bind to CollectionViewSource.