I have a WPF project and I would like to have a listbox with checkbox on each ListboxItem and have an ObservableCollection to store checked ListboxItems.
I need to move checked chars from one ObservableCollection to another ObservableCollection.
With this code I can select multiple checkboxes but when I trigger the command MoveChar (command to move chars to another ObservableCollection) via the button only one ListboxItem moves and I need to click it more times to move all the checked chars.
View
<ListBox SelectionMode="Multiple" ItemsSource="{Binding Chars}" SelectedIndex="{Binding SelectedCharsIndex, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding SelectedChars, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Char}" HorizontalAlignment="Center" />
<TextBlock Text="{Binding Description}" Grid.Column="1" HorizontalAlignment="Left"/>
<CheckBox Grid.Column="2" IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Path=IsSelected}" HorizontalAlignment="Right" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ViewModel
public CharModel SelectedChars {
get { return _selectedChars; }
set { _selectedChars = value; NotifyPropertyChanged(); }
}
MoveChar = new RelayCommand(
() =>
{
if (SelectedChars != null)
{
TestChars.Add(SelectedChars);
Chars.Remove(SelectedChars);
Selected = false;
}
});
When I change SelectedChars to ObservableCollection<CharModel> it doesn´t work at all. In ViewModel I iterate each item via foreach.
You are binding SelectedChars to the SelectedItem property which is for use with Single selection listboxes and will only return a single object, not a collection. Try binding to the SelectedItems property. Be sure the "Items" part is plural. You will need to refactor the property type to be a SelectedObjectCollection as well but this inherits IList so you can iterate the same.
Related
I want to create a simple window that would display different controls (SpinEdit or TextEdit) based on the view-model that is selected.
I have the code and logic behind it done already, what is left is displaying the control (SpinEdit or TextEdit) itself.
XAML:
<dx:DXWindow.Resources>
<DataTemplate x:Key="DataTemplate_Value">
<dxe:SpinEdit Height="23" MinWidth="200" Width="Auto"
Text="{Binding Path=Value, Mode=TwoWay}"
Mask="{Binding Mask, Mode=OneWay}"
MaxLength="{Binding Path=InputLength}" />
</DataTemplate>
<DataTemplate x:Key="DataTemplate_Text">
<dxe:TextEdit Height="23" MinWidth="200" Width="Auto"
Text="{Binding Path=Value, Mode=TwoWay}"
MaskType="RegEx" Mask="{Binding Mask, Mode=OneWay}"
MaxLength="{Binding Path=InputLength}"/>
</DataTemplate>
<local:PropertyDataTemplateSelector x:Key="templateSelector"
DataTemplate_Value="{StaticResource DataTemplate_Value}"
DataTemplate_Text="{StaticResource DataTemplate_Text}" />
</dx:DXWindow.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel Grid.Row="0" >
<Label x:Uid="Label" MinHeight="24" MinWidth="60" Content="Value" />
<ContentControl ContentTemplateSelector="{StaticResource templateSelector}" />
</StackPanel>
<StackPanel Grid.Row="1" x:Uid="OKCancel_Buttons" Orientation="Horizontal" HorizontalAlignment="Right" VerticalAlignment="Bottom">
<Button Height="23" x:Name="OK_Button" Click="OK_Click" Content="OK" IsDefault="True" HorizontalAlignment="Right" MinWidth="95" />
<Button Height="23" x:Name="Cancel_Button" Click="Cancel_Click" Content="Cancel" HorizontalAlignment="Right" MinWidth="95" />
</StackPanel>
Where in the <ContentControl> I want to select which control will be displayed (SpinEdit for numbers and TextEdit for names/letters)
C#:
public class PropertyDataTemplateSelector : DataTemplateSelector
{
public DataTemplate DataTemplate_Value { get; set; }
public DataTemplate DataTemplate_Text { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var selector = item as TInputBaseVM;
if(selector is TInputValueVM)
return DataTemplate_Value;
return DataTemplate_Text;
}
}
Where I want to return a specific DataTemplate based on the view-model that is created in the c++/cli code.
C++/cli:
TInputValueVM ^oExchange_Value;
TInputTextVM ^oExchange_Text;
int inputFormat = A_Attributes.GetInputFormat();
if(inputFormat)
oExchange_Text = gcnew TInputTextVM(gcnew System::String(A_Attributes.GetTitle()), gcnew System::String(A_Attributes.GetMask()),
A_Attributes.GetInputLength(), gcnew System::String(A_Attributes.GetInitialText()));
else
oExchange_Value = gcnew TInputValueVM(gcnew System::String(A_Attributes.GetTitle()), gcnew System::String(A_Attributes.GetMask()),
A_Attributes.GetInputLength(), A_Attributes.GetInitialValue());
Dialogs::TSignalNumberPositionDialog^ dialog = gcnew Dialogs::TSignalNumberPositionDialog();
if(inputFormat)
dialog->DataContext = oExchange_Text;
else
dialog->DataContext = oExchange_Value;
dialog->ShowDialog();
The point is, the item value in the overriden selector function always has the null value and I have no idea how to bind it in XAML since all the examples I've managed to find so far are ListBoxes etc. There's no example on how to display different controls based on the view-model.
EDIT:
As suggested, I added Content property in the ContentControl and passed it an argument which is now the 'item' argument in the selector. Works just fine!
You do not need a DataTemplateSelector. WPF provides a mechanism that automatically selects a DataTemplate for the ContentTemplate of a ContentControl according to the type of a Content.
As explained in DataTemplate.DataType:
When you set this property to the data type without specifying an x:Key, the DataTemplate gets applied automatically to data objects of that type.
So drop the x:Key value and your DataTemplateSelector, set DataType
<dx:DXWindow.Resources>
<DataTemplate DataType="{x:Type local:TInputValueVM}">
<dxe:SpinEdit Height="23" MinWidth="200" Width="Auto"
Text="{Binding Path=Value, Mode=TwoWay}"
Mask="{Binding Mask, Mode=OneWay}"
MaxLength="{Binding Path=InputLength}" />
</DataTemplate>
<DataTemplate DataType="{x:Type local:TInputTextVM}">
<dxe:TextEdit Height="23" MinWidth="200" Width="Auto"
Text="{Binding Path=Value, Mode=TwoWay}"
MaskType="RegEx" Mask="{Binding Mask, Mode=OneWay}"
MaxLength="{Binding Path=InputLength}"/>
</DataTemplate>
</dx:DXWindow.Resources>
and bind the ContentControl's Content to a property that returns either a TInputValueVM or a TInputTextVM:
<ContentControl Content="{Binding InputVM}" />
The appropriate DataTemplate will now be selected automatically.
You have to add some value in the Content property of the ContentControl. That value will be passed to the SelectTemplate as the object item. You probably should bind to it some property in your ViewModel to be able to change that from there.
I'm trying to change the Visibility of elements inside a ListBoxItem by clicking on a CheckBox outside the ListBox which contains Items, but it doesn't seem to work.
It looks like the the binding doesn't work inside the ListBoxItems.
I'm using a template for my items
XAML
<UserControl.Resources>
<local:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
<DataTemplate x:Key="ShotTemplate">
<Grid x:Name="GridItem" Width="200">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox x:Name="ShotBox" Grid.Column="1" Text="{Binding Path=Description}" Visibility="{Binding EditMode, ElementName=EditMode, Converter={StaticResource BooleanToVisibilityConverter}}" />
<TextBlock x:Name="ShotBlock" Grid.Column="1" Text="{Binding Path=Description}" Visibility="{Binding EditMode, Converter={StaticResource BooleanToVisibilityConverter}, ConverterParameter=False }" />
</Grid>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ListBox Name="ShotList" ItemsSource="{Binding AllShotsCollection}" ItemTemplate="{StaticResource ShotTemplate}"/>
<CheckBox Name="EditMode" IsChecked="{Binding EditMode}" Content="Edit Mode" HorizontalAlignment="Left" Margin="12,30,0,0" VerticalAlignment="Top"/>
</Grid>
ViewModel
private bool _editMode = true;
public bool EditMode
{
get { return _editMode; }
set { _editMode = value; RaisePropertyChanged("EditMode"); }
}
How do I change ShotBox and ShotBlock Visibility by checking or unchecking the CheckBox. I know the converter works correctly, that's not the problem it must have something to do with the binding.
The ElementName binding scope is within the template only. I would define an attached property on the ListView (not the ListViewItem) and have the Checkbox toggle that property. Within the DataTemplate, you'll be able to use RelativeSource / FindAncestor binding to find the ListView.
This line of xaml code is not working. You are trying to bind to the view element and viewmodel prop. at the same time.
<TextBox x:Name="ShotBox" Grid.Column="1" Text="{Binding Path=Description}" Visibility="{Binding EditMode, ElementName=EditMode, Converter={StaticResource BooleanToVisibilityConverter}}" />
Remove this 'ElementName=EditMode' so that it would bind properly to the viewmodel property 'EditMode'.
OR if you want to bind to the view element only use
Visibility="{Binding Path=IsChecked, ElementName=EditMode, Converter={StaticResource BooleanToVisibilityConverter}}" />
I have a WPF application with a DataGrid (from WPF Toolkit) control. The ItemsSouce property is bound to an ObservableCollection in my ViewModel.
The data grid has a column with a TextBox in it:
<dg:DataGrid.Columns>
<dg:DataGridTemplateColumn Header="Name">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding UserName}" Width="300"/>
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
...
I also have an "Add" button to create a new user. When I click this, a new row is created. I would like, however, for the above textbox to get the input focus (on the new row of course). I have looked at:
WPF MVVM Focus Field on Load
WPF-MVVM: Setting UI control focus from ViewModel
How to set focus to textbox using MVVM?
Set focus on textbox in WPF from view model (C#)
But all of them seem to rely on same variation of an "ElementName" binding and none look like they would work in an ItemsControl. What is the correct way to get this behavior?
One way I believe you can do this is to have a trigger on the textbox that handles on Loaded and have it set focus. Something like this
<dg:DataGridTemplateColumn Header="Name">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" Text="{Binding UserName}" Width="300">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<local:SetFocusTrigger/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
</dg:DataGridTemplateColumn>
And the SetFocusTrigger class is :-
public class SetFocusTrigger : TargetedTriggerAction<Control>
{
protected override void Invoke(object parameter)
{
if (Target == null) return;
Target.Focus();
}
}
Note I haven't tried this out.
I tried to do in pure XAML
<DataGrid CanUserAddRows="True"
ItemsSource="{Binding List}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"
Width="300" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Name}"
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}"
Width="300" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I added two templates 1 for normal and other for edit mode. so once cell enter edit mode it will focus the text box.
if you always want the text box to be visible then it would have a different behavior as the new row is binded to placeholder and the value is text box will persist and move over to every new row but the real row.
ListView Solution (MVVM)
sample XAML
<StackPanel>
<Button Command="{Binding AddItem}"
Content="Add Item"/>
<ListView ItemsSource="{Binding List}"
Grid.IsSharedSizeScope="True">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="name" />
<!--other columns-->
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBox Text="{Binding Name}"
FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}" />
<TextBox Text="other column" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
sample VM
public ViewModel()
{
AddItem = new SimpleCommand(i => List.Add(new Person()));
List = new ObservableCollection<object>(new[] { new Person() { Name = "a person"} });
}
public ObservableCollection<object> List { get; set; }
class Person
{
public string Name { get; set; }
}
public ICommand AddItem { get; set; }
so upon adding new item the new row will be created by means of adding a new Person to the List and will be displayed in view with the focus on the text box.
run the sample above and see the behavior, no code behind involved.
I have a collection with fields cityname, statename and countryname and I bind that collection to my wpf form. I want to display the cityname in a Textbox, the statename in a combobox and the countryname in a combobox. All the textboxes and comboboxes should come dynamically. How can I do this job?
Any one suggest me how to design this form dynamically in wpf using MVVM I am trying to do this code but not get result properly. Either I get everything as textbox or combobox, but what i need is textbox and combobox as specified.
<Border Margin="3.5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="125" />
<ColumnDefinition Width="*" MinWidth="100" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock x:Name="tbFieldTag" Cursor="Hand" VerticalAlignment="Center" HorizontalAlignment="Stretch" TextWrapping="Wrap" Margin="10,0,0,0" Text="{Binding Path=CardField.FieldTag}" />
<TextBox Margin="10,0,0,0" x:Name="txtFieldData" Grid.Column="1" MaxLength="{Binding Path=CardField.MaximumLength}" Text="{Binding Path=CardField.FieldData, Mode=TwoWay}" />
<!--<ComboBox Margin="10,0,0,0" x:Name="comboFieldData" Grid.Column="1" Text="{Binding Path=CardField.FieldTag}"/>-->
</Grid>
</Border>
The key to your problem are DataTemplates. These allow you to bind your view to a collection of custom objects.
You should have a ViewModel that is exposing an ObservableCollection<TLocation> where TLocation is a class that is exposing public properties Cityname, Statename and Countryname.
In your View you need to show a ContentControl, say a ListBox, having it's ItemSource property bound to the ObservableCollection.
Then you set the DataTemplate for the Listbox to something like:
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBox Text="{Binding Path=CityName}" />
<ComboBox Text="{Binding Path=StateName}" />
<ComboBox Text="{Binding Path=CountryName}" />
</StackPanel>
</DataTemplate>
Another approach is to use a DataGrid. See this article
I am trying to populate a combobox, which is part of an itemscontrol, with a list of items (ParentCredentials). The problem is that these ParentCredentials are at the same level of the items being binded with the itemscontrol. Not sure if this is clear but if you have a look at the view model it should make more sense
This is my viewmodel:
public class AccessControlViewModel : INotifyPropertyChanged
{
public ObservableCollection<LogonCredential> Credentials
{...}
public List<string> ParentCredentials
{...}
}
And i have the following XAML.
<ItemsControl ItemsSource="{Binding AccessControl.Credentials}" HorizontalContentAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid >
<Grid.ColumnDefinitions >
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="{Binding Path=DisplayName}"/>
<ComboBox Grid.Column="2" ItemsSource="{Binding Source={RelativeSource AncestorType={x:Type vm:ResourceViewModel}}, Path=AccessControl.ParentCredentials}">
</ComboBox>
...
How can I make this binding? Also note that AccessControl is a part of ResourceViewModel class.
You need to navigate back up to the ItemsControl, and bind via the DataContext path.
{Binding RelativeSource={RelativeSource AncestorType=ItemsControl}, Path=DataContext.AccessControl.ParentCredentials}
Source={RelativeSource... never works in any case. Further the AncestorType is always some FrameworkElement rather than a data-object.