I have the following scenario:
Problem : Cant bind a property as enum to combox in wpf mvvm ?
How can I bind this enum to a combobox?
1.I have an enum.
public enum RankType
{
StringValue1,
StringValue2,
StringValue3
}
2.I Have a property as enum in myclass :
[DefaultValue(RankType.StringValue1)]
[ConvertUsing(typeof(EnumTypeConverter<RankType>))]
public RankType Rank { set; get; }
ConvertUsing Class,It does the conversion for me:
[System.AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public class ConvertUsingAttribute:Attribute
{
private TypeConverter _converter = null;
public TypeConverter Converter
{
get
{
if (_converter == null)
_converter = (TypeConverter)System.Activator.CreateInstance(TypeOfConverter);
return _converter;
}
}
public Type TypeOfConverter
{
get;
private set;
}
public ConvertUsingAttribute(Type converterType)
{
this.TypeOfConverter = converterType;
}
}
EnumTypeConverter Class:
public class EnumTypeConverter<T>:TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return sourceType == typeof(string);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
return destinationType == typeof(string);
}
public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
{
if (value != null && value is string)
{
return Enum.Parse(typeof(T), (string)value);// int.Parse(((string)value).Trim());
}
return base.ConvertFrom(context, culture, value);
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
if (value != null && destinationType == typeof(string))
{
return ((T)value).ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
4.In ViewModel
private MilitaryRankType _selectedRankType;
public RankType SelectedRankType
{
get { return _selectedRankType; }
set
{
_selectedRankType = value;
NotifyPropertyChanged(nameof(RankTypes));
}
}
private RankType[] _rankTypes;
public RankType[] RankTypes
{
get
{
return _rankTypes ??
(_rankTypes =Enum.GetValues(typeof(RankType)).Cast<RankType>().ToArray());
}
}
5.In View
<ComboBox ItemsSource="{Binding RankTypes, UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" SelectedItem="{Binding SelectedRankType}" />
6.I used the listview in xaml
<ListView ItemsSource="{Binding EmployeesList}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="3">
<ListView.View>
<GridView>
<GridViewColumn Width="150" Header="">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=NationalId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="150" Header="">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=CardId, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="150" Header="">
<GridViewColumn.CellTemplate>
<DataTemplate>
**<ComboBox SelectedItem="{Binding SelectedRankType}" />**
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="150" Header="">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="150" Header="">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=LastName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,NotifyOnValidationError=True,ValidatesOnDataErrors=True,ValidatesOnExceptions=True}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Honestly, I don't really know what went wrong in your code, but I can give you a working example without any fancy converter stuff:
Suppose you have the following window-content:
<Grid x:Name="grid1">
<ComboBox ItemsSource="{Binding SelectableRanks}" SelectedItem="{Binding SelectedRank}"/>
</Grid>
And you initialize the datacontext in the window constructor:
public MainWindow()
{
InitializeComponent();
grid1.DataContext = new RankSelectionVM();
}
And this is your viewmodel:
public class RankSelectionVM
{
private RankType _SelectedRank;
public RankType SelectedRank
{
get { return _SelectedRank; }
set { _SelectedRank = value; }
}
private RankType[] _rankTypes;
public RankType[] SelectableRanks
{
get
{
return _rankTypes ??
(_rankTypes = Enum.GetValues(typeof(RankType)).Cast<RankType>().ToArray());
}
}
}
public enum RankType
{
StringValue1,
StringValue2,
StringValue3
}
It doesn't have any INotifyPropertyChanged or any converters attached, but place a breakpoint in the SelectedRank setter - it should break when you select a value. If this code works for you, you have to find what you did differently to get a not-working code in your project. Otherwise you may have some very strange issue that needs special care.
Related
When the cell in the column is in focus the ComboBox shall appear but once the value is selected and the cell is not in focus anymore, only the text shall appear. So the ComboBox shall only be visible when cell is in focus.
This is my code but I've really no clue how to solve that.
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="SchichtID" Width="60">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="SelectedShiftHID"
SelectedIndex="{Binding SchichtID}"
DisplayMemberPath="Bezeichnung"
ItemsSource="{Binding DataContext.UiShiftHModelList, Mode=OneWay,RelativeSource={RelativeSource AncestorType=ListView},UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
1.EDIT:
What I'm trying here is to put combobox into a column of a ListView. The values published there come from Model A. The DisplayedMemberPath is the description of the row from model a. We save the ID of that row from Model A in Model B. When the data is reloaded the correct description shall be loaded and shown again in the way explained in my initial post.
2.EDIT:
#Anton (that guy deleted his answer?) - your answer doesn't work. It starts that there is no comboBox shown when focussing the cell neither it shows any text.
In the XAML of the View im introducing the converters:
<UserControl.Resources>
<helpers:LastRowVisibilityMultiValueConverter x:Key="LastRowVisibilityMultiValueConverter" />
<helpers:ShiftHIDtoDescriptionConverter x:Key="ShiftHIDtoDescriptionConverter" ShiftH="{Binding DataContext.UiShiftHModelList, Mode=OneWay, ElementName=ShiftT, UpdateSourceTrigger=PropertyChanged}"/>
<helpers:CellTemplateSelector x:Key="cellTemplateSelector" x:Name="cellTemplateSelector">
<helpers:CellTemplateSelector.EditableTemplate>
<DataTemplate>
<ComboBox x:Name="SelectedShiftHID"
SelectedIndex="{Binding ID}"
DisplayMemberPath="Bezeichnung"
ItemsSource="{Binding UiShiftHModelList, Mode=OneWay,ElementName=ShiftT,UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</helpers:CellTemplateSelector.EditableTemplate>
<helpers:CellTemplateSelector.ReadOnlyTemplate>
<DataTemplate>
<TextBlock Text="{Binding SchichtID, Converter={StaticResource ShiftHIDtoDescriptionConverter}}"/>
</DataTemplate>
</helpers:CellTemplateSelector.ReadOnlyTemplate>
</helpers:CellTemplateSelector>
</UserControl.Resources>
There simply happens nada.
One error I've got in your suggested converter was:
public static readonly DependencyProperty ItemsProperty = DependencyProperty.Register("Items", typeof(IEnumerable), typeof(ItemIdToStringConverter:DependencyObject), new PropertyMetadata(null));
This here: typeof(ItemIdToStringConverter:DependencyObject)
Following the adjusted converter:
public class ShiftHIDtoDescriptionConverter : DependencyObject, IValueConverter
{
public static readonly DependencyProperty ShiftHProperty = DependencyProperty.Register("ShiftH", typeof(IEnumerable), typeof(ShiftHIDtoDescriptionConverter),new PropertyMetadata(null));
public IEnumerable ShiftH
{
get { return (IEnumerable)GetValue(ShiftHProperty); }
set { SetValue(ShiftHProperty, value); }
}
public object Convert(object shiftHID, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
int? id = shiftHID as int?;
if (id != null) {
return ShiftH.Cast<UiShiftHModel>().FirstOrDefault(m => m.ID == id)?.Bezeichnung;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
throw new NotImplementedException();
}
}
This here is the XAML part:
<Border Grid.Row="1" Grid.Column="1"
Margin="10,10,10,10"
BorderBrush="#FF474A57"
CornerRadius="10,10,10,10"
BorderThickness="2,2,2,2"
Width="520"
MaxHeight="300"
Background="White">
<StackPanel Margin="0,0,0,20" Orientation="Vertical">
<StackPanel Grid.Column="0" Grid.RowSpan="1"
Grid.Row="1"
VerticalAlignment="Top">
<Label HorizontalAlignment="Center" FontWeight="Bold">
Schichtdetails
</Label>
<ListView x:Name="ShiftT" MinHeight="150" MaxHeight="200" MinWidth="500" HorizontalContentAlignment="Stretch" HorizontalAlignment="Center"
ItemContainerStyle="{DynamicResource DifAlternationColorsLV}"
AlternationCount="2"
ItemsSource="{Binding UiShiftTModelList, UpdateSourceTrigger=PropertyChanged}" d:ItemsSource="{d:SampleData ItemCount=5}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="ID" Width="30">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox x:Name="ID" MinWidth="30"
Style="{StaticResource TBoxInListV}"
Text="{Binding ID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
BorderThickness="0">
</TextBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="SchichtID" Width="60">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ContentControl ContentTemplateSelector="{StaticResource cellTemplateSelector}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</StackPanel>
</StackPanel>
</Border>
Watching the CellTemplateSelector with a breakpoint shows:
public class CellTemplateSelector : DataTemplateSelector
{
//Answer for question: switch appearance of the ListView column from combobox to textbox
//https://stackoverflow.com/questions/73046926/wpf-column-in-listview-shall-represent-a-combobox-when-isfocused-true-but-a-si/73048416?noredirect=1#comment129022042_73048416
public DataTemplate EditableTemplate { get; set; }
public DataTemplate ReadOnlyTemplate { get; set; }
public override DataTemplate
SelectTemplate(object item, DependencyObject container) {
ContentControl contentControl = container as ContentControl;
if (contentControl != null) {
if (contentControl.IsFocused)
return EditableTemplate;
else
return ReadOnlyTemplate;
}
return null;
}
that the contentControl is always null.
3.EDIT
I guess its not a ContentControl its rather a ContenPresenter. Then the casting works. But now I'm fucked up with Binding Errors:
4.EDIT
Oh, there is another problem with the converter for the id to description. The code therefor from a yet deleted answer is completely bs. The passed id has to be looked up in the UiShiftHModel but there is no chance to pass the collection into the converter. Maybe via multi binding converter....
First of all.. better to create own customControl with properties which allow you to switch templates for Readonly and Editable templates
public class InteractiveContentControl : ContentControl
{
public static readonly DependencyProperty IsEditableProperty =
DependencyProperty.Register("IsEditable", typeof(bool), typeof(InteractiveContentControl), new FrameworkPropertyMetadata(false, OnIsEditablePropertyChanged));
public bool IsEditable
{
get { return (bool)GetValue(IsEditableProperty); }
set { SetValue(IsEditableProperty, value); }
}
private static void OnIsEditablePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as InteractiveContentControl;
control.ChangeTemplate();
}
public static readonly DependencyProperty EditableTemplateProperty = DependencyProperty.Register("EditableTemplate", typeof(DataTemplate), typeof(InteractiveContentControl), new PropertyMetadata(null));
public DataTemplate EditableTemplate
{
get { return (DataTemplate)GetValue(EditableTemplateProperty); }
set { SetValue(EditableTemplateProperty, value); }
}
public static readonly DependencyProperty ReadonlyTemplateProperty = DependencyProperty.Register("ReadonlyTemplate", typeof(DataTemplate), typeof(InteractiveContentControl), new PropertyMetadata(null));
public DataTemplate ReadonlyTemplate
{
get { return (DataTemplate)GetValue(ReadonlyTemplateProperty); }
set { SetValue(ReadonlyTemplateProperty, value); }
}
public InteractiveContentControl():base()
{
DefaultStyleKey = typeof(ContentControl);
this.Loaded += OnLoaded;
this.LostFocus += OnLostFocus;
this.IsKeyboardFocusWithinChanged += InteractiveContentControl_IsKeyboardFocusWithinChanged;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
ChangeTemplate();
}
private void InteractiveContentControl_IsKeyboardFocusWithinChanged(object sender, DependencyPropertyChangedEventArgs e)
{
IsEditable = IsKeyboardFocusWithin;
}
private void OnLostFocus(object sender, RoutedEventArgs e)
{
IsEditable = IsKeyboardFocusWithin;
}
private void ChangeTemplate()
{
ContentTemplate = IsEditable ? EditableTemplate : ReadonlyTemplate;
}
protected override void OnPreviewMouseDown(MouseButtonEventArgs e)
{
base.OnPreviewMouseDown(e);
IsEditable = true;
}
}
Also need to have class for convert Id to the Name from the comboBox.
public class ShiftHIDtoDescriptionConverter : DependencyObject, IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
int? id = (int?)values[0];
IEnumerable<UiShiftHModel> items = values[1] as IEnumerable<UiShiftHModel>;
if (id!=null && items!=null)
{
return items.FirstOrDefault(i => i.ID == id)?.Bezeichnung;
}
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then you can define your resources in xaml
<UserControl.Resources>
<helpers:ShiftHIDtoDescriptionConverter x:Key="ShiftHIDtoDescriptionConverter" />
<DataTemplate x:Key="EditableTemplate" DataType="UIShiftTModel">
<ComboBox x:Name="SelectedShiftHID"
ItemsSource="{Binding DataContext.UiShiftHModelList, Mode=OneWay, ElementName=ShiftT, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="ID"
SelectedValue="{Binding SchichID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
DisplayMemberPath="Bezeichnung"/>
</DataTemplate>
<DataTemplate x:Key="ReadonlyTemplate" DataType="UIShiftTModel">
<Grid>
<TextBlock HorizontalAlignment="Stretch">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource ShiftHIDtoDescriptionConverter}">
<Binding Path="SchichID" />
<Binding Path="DataContext.UiShiftHModelList" ElementName="ShiftT" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</Grid>
</DataTemplate>
</UserControl.Resources>
And then you will be able to define your GridView
<ListView x:Name="ShiftT" MinHeight="150"
AlternationCount="2"
ItemsSource="{Binding UiShiftTModelList, UpdateSourceTrigger=PropertyChanged}" d:ItemsSource="{d:SampleData ItemCount=5}">
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="ID" Width="30">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="ID" MinWidth="30"
Text="{Binding ID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="SchichtID" x:Name="colSchichtID">
<GridViewColumn.CellTemplate>
<DataTemplate>
<helpers:InteractiveContentControl Content="{Binding}" Width="200"
EditableTemplate="{StaticResource EditableTemplate}"
ReadonlyTemplate="{StaticResource ReadonlyTemplate}"
/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
So i have this ListView with ItemSource of my object and ListViewItem:
<GridViewColumn Header="Select" Width="45">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Command="{Binding SelectedInterfaceCommand}"
CommandParameter="{Binding IsChecked, RelativeSource={RelativeSource Self}}">
</CheckBox>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
ViewModelBase
public ViewModelBase()
{
SelectInterfaceCommand = new SelectedInterfaceCommand(this);
}
And i try to catch my CheckBox IsChecked event inside my SelectedInterfaceCommand Command:
public class SelectedInterfaceCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public ViewModelBase ViewModel { get; set; }
public SelectedInterfaceCommand(ViewModelBase viewModel)
{
ViewModel = viewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
}
}
And my CanExecute and Execute never called.
I also try to this approach:
Inside my Object class i have this Property:
public bool IsSelected
{
get { return isSelected; }
set
{
isSelected = value;
OnStaticPropertyChanged();
}
}
XAML
<GridViewColumn Header="Select" Width="45">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox
<CheckBox.IsChecked>
<Binding Path="IsSelected" RelativeSource="{RelativeSource AncestorType={x:Type ListViewItem}}"/>
</CheckBox.IsChecked>
</CheckBox>
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
And again this IsSelected setter never called.
I am using a ListView in wpf mvvm pattern whose SelectedItem binding is done to the ViewModel. The problem what I am facing is as soon as I check the checkbox, The SelectedItem binding is not working immediately. It work only when I click again somewhere outside the checkbox and its respective content.
My ListView is like this:
<Grid>
<Grid.Resources>
<DataTemplate x:Key="checkboxHeaderTemplate">
<CheckBox IsChecked="{Binding Path=DataContext.AllSelected,RelativeSource={RelativeSource AncestorType=UserControl },Mode=TwoWay}">
</CheckBox>
</DataTemplate>
<DataTemplate x:Key="CheckBoxCell">
<!--<CheckBox Checked="CheckBox_Checked" />-->
<CheckBox IsChecked="{Binding Path=IsSelected, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
</CheckBox>
</DataTemplate>
<DataTemplate x:Key="TextCell">
<TextBlock Text="Usecasename">
</TextBlock>
</DataTemplate>
<DataTemplate x:Key="ButtonCell">
<Button Content="{Binding Path=UsecaseName, Mode=TwoWay}" >
</Button>
</DataTemplate>
</Grid.Resources>
<ListView SelectedItem="{Binding SelectedSection}" ItemsSource="{Binding Path=UsecaseListItems}" >
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn HeaderTemplate="{StaticResource checkboxHeaderTemplate}"
CellTemplate="{StaticResource CheckBoxCell}" Width="auto">
</GridViewColumn>
<GridViewColumn HeaderTemplate="{StaticResource TextCell}"
CellTemplate="{StaticResource ButtonCell}" Width="auto">
</GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
</Grid>
The HomeViewModel with which I am binding the Selected itm of List View is like this:
private UseCase _selectedSection;
public UseCase SelectedSection
{
get { return _selectedSection; }
set
{
_selectedSection = value;
if (this.SelectedSection.UsecaseName == ("CCS01") && (this.SelectedSection.IsSelected == true))
{
this.ContentWindow = new CCS01();
}
else if (this.SelectedSection.UsecaseName == ("CCS02") && (this.SelectedSection.IsSelected == true))
{
this.ContentWindow = new CCS02();
}
else if (this.SelectedSection.UsecaseName == ("ECS52") && (this.SelectedSection.IsSelected == true))
{
this.ContentWindow = new ECS52();
}
else
this.ContentWindow = new Default();
OnPropertyChanged("SelectedSection");
}
}
and The UseCase class is this:
public class UseCase: BaseNotifyPropertyChanged
{
public string UsecaseName { get; set; }
private bool _IsSelected;
public bool IsSelected
{
get { return _IsSelected; }
set
{
_IsSelected = value;
OnPropertyChanged("IsSelected");
}
}
}
Please suggest what correction should I do so that, It should hit the binding directly as I check the Checkboxes.
You should change the checkbox binding of the CheckBoxCell to something like this :
<DataTemplate x:Key="CheckBoxCell">
<!--<CheckBox Checked="CheckBox_Checked" />-->
<CheckBox IsChecked="{Binding Path=IsSelected,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListViewItem}},
Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
</CheckBox>
</DataTemplate>
And for this work you need to put SelectionMode to Single
<ListView SelectedItem="{Binding SelectedSection}"
ItemsSource="{Binding Path=UsecaseListItems}" SelectionMode="Single">
And do this in your viewmodel
private UseCase _selectedSection;
public UseCase SelectedSection
{
get { return _selectedSection; }
set
{
DeSelectAll();
_selectedSection = value;
_selectedSection.IsSelected = true;
OnPropertyChanged("SelectedSection");
}
}
private void DeSelectAll()
{
foreach (var item in UsecaseListItems)
{
item.IsSelected = false;
}
}
I have this view model:
public class MyData
{
public string Status;
public StatusMsg StatusMessage;
private Brush _statusBrushes;
public Brush StatusBrushes
{
get
{
switch (StatusMessage)
{
case StatusMsg.Cancel:
return Brushes.Red;
case StatusMsg.InProcess:
return Brushes.Blue;
case StatusMsg.Done:
return Brushes.Green;
default:
return Brushes.Green;
}
}
set { _statusBrushes = value; }
}
public enum StatusMsg
{
Cancel,
Done,
InProcess,
}
}
Now i have this GridViewColumn:
<GridViewColumn Width="180" Header="Status">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="Txt" Text="{Binding Status}" Foreground="Yellow" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
So as you can see this GridViewColumn color is yellow and i want to change it according my StatusMsg (my enum) so my question is how to bind my color into my XAML ?
I would recommend creating an IValueConverter that is able to convert your enum value to the appropriate color and then your binding would look something like:
<GridViewColumn Width="180" Header="Status">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock x:Name="Txt" Text="{Binding Status}"
Foreground="{Binding Path=StatusColor, Converter={StaticResource MyStatusColorConverter}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
You will of course also need to create the resource, here's a tutorial that a quick bing search turned up: http://wpftutorial.net/ValueConverters.html
Is it possible to bind a control's TabIndex to the column's order in a GridView? Say, we have a GridView with AllowsColumnReorder set to true, and when we dragging a second column to be last, the tab navigation order would remain column-ordered: 1 -> 3 -> 2, and not the 1-> 2 -> 3 as usually. What i want to do is the tab navigation according to real column layout as on second image.
My code for kxaml:
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<ListView ItemsSource="2"
Grid.Row="1">
<ListView.View>
<GridView AllowsColumnReorder="True">
<GridViewColumn Header="One">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Width="100" Text="1"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Two">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Width="100" Text="2"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Three">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Width="100" Text="3"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
</Page>
here is an example with datagrid instead
<DataGrid ItemsSource="2" Grid.Row="1">
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False" />
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTemplateColumn Header="One">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Width="100" Text="1" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Two">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Width="100" Text="2" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Three">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Width="100" Text="3" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
EDIT
here is a solution with listview that works, but i think its not the best...
public class CustomGridViewColumn : GridViewColumn
{
public static readonly DependencyProperty ColumnIndexProperty =
DependencyProperty.Register("ColumnIndex", typeof(int), typeof(CustomGridViewColumn),
new FrameworkPropertyMetadata());
public int ColumnIndex {
get { return (int)GetValue(ColumnIndexProperty); }
set { SetValue(ColumnIndexProperty, value); }
}
}
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
gridView.Columns.CollectionChanged+= new NotifyCollectionChangedEventHandler(gridView_Columns_CollectionChanged);
}
void gridView_Columns_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
var index = 0;
foreach(CustomGridViewColumn col in gridView.Columns){
col.ColumnIndex=index++;
}
}
}
<ListView KeyboardNavigation.TabNavigation="Cycle">
<ListView.View>
<GridView x:Name="gridView" AllowsColumnReorder="True">
<local:CustomGridViewColumn Header="One" x:Name="col1">
<local:CustomGridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Width="100" Text="1" TabIndex="{Binding Path=ColumnIndex, Mode=OneWay, ElementName=col1}"/>
</DataTemplate>
</local:CustomGridViewColumn.CellTemplate>
</local:CustomGridViewColumn>
<local:CustomGridViewColumn Header="Two" x:Name="col2">
<local:CustomGridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Width="100" Text="2" TabIndex="{Binding Path=ColumnIndex, Mode=OneWay, ElementName=col2}"/>
</DataTemplate>
</local:CustomGridViewColumn.CellTemplate>
</local:CustomGridViewColumn>
<local:CustomGridViewColumn Header="Three" x:Name="col3">
<local:CustomGridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Width="100" Text="3" TabIndex="{Binding Path=ColumnIndex, Mode=OneWay, ElementName=col3}"/>
</DataTemplate>
</local:CustomGridViewColumn.CellTemplate>
</local:CustomGridViewColumn>
</GridView>
</ListView.View>
<ListViewItem>1</ListViewItem>
<ListViewItem>2</ListViewItem>
<ListViewItem>3</ListViewItem>
</ListView>
hope this helps
If you use a DataGrid instead: The columns have a DisplayIndex property, which holds the current index, even when they are reordered.
Here's a big start. The only thing left to do would be to walk the GridView's ListView's Items and set tab index foreach element in the row (//! iterate ListView rows). Some of the logic may be messed up, as code was rushed..
XAML
<GridView
AllowsColumnReorder="True"
Controls:GridViewExtensions.DoTabIndexing="True">...
C#
/// <summary>Provides members helpful to <see cref="GridView"/>.</summary>
public static class GridViewExtensions
{
#region DoTabIndexing
[Category("Common")]
[AttachedPropertyBrowsableForType(typeof(GridView))]
public static bool GetDoTabIndexing(GridView gridView)
{
return (bool)gridView.GetValue(DoTabIndexingProperty);
}
public static void SetDoTabIndexing(GridView gridView, bool value)
{
gridView.SetValue(DoTabIndexingProperty, value);
}
public static readonly DependencyProperty DoTabIndexingProperty =
DependencyProperty.RegisterAttached(
"DoTabIndexing",
typeof(bool), // type
typeof(GridViewExtensions), // container/holder/control
new PropertyMetadata(default(bool), OnDoTabIndexingChanged)
);
private static void OnDoTabIndexingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var gridView = (GridView)d;
if (gridView.AllowsColumnReorder == false) { return; }
var newValue = (bool)e.NewValue;
_indexWatch = new ColumnIndexWatch(gridView);
}
static ColumnIndexWatch _indexWatch;
#endregion DoTabIndexing
/// <summary>Watches for changes in a <see cref="GridView"/>'s columns.</summary>
class ColumnIndexWatch
{
readonly GridView _gridView;
public ColumnIndexWatch(GridView gridView)
{
_gridView = gridView;
gridView.Columns.CollectionChanged += OnItemsPopulated;
}
void OnItemsPopulated(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action != NotifyCollectionChangedAction.Add)
{
_gridView.Columns.CollectionChanged -= OnItemsPopulated;
_gridView.Columns.CollectionChanged += OnItemMoved;
trax = new ColumnIndexCollection(_gridView.Columns);
OnItemMoved(sender, e);
}
}
ColumnIndexCollection trax;
void OnItemMoved(object sender, NotifyCollectionChangedEventArgs e)
{
var movedColumn = e.NewItems[0] as GridViewColumn;
if (movedColumn == null) { return; }
trax.ApplyNewIndex(movedColumn, e.NewStartingIndex);
}
/// <summary>Represents a collection of <see cref="ColumnIndex"/></summary>
class ColumnIndexCollection : Collection<ColumnIndex>
{
public ColumnIndexCollection(IEnumerable<GridViewColumn> columns)
: base(Create(columns)) { }
static IList<ColumnIndex> Create(IEnumerable<GridViewColumn> columns)
{
return columns.Select((t, i) => new ColumnIndex { GridViewColumn = t, Index = i }).ToList();
}
public void ApplyNewIndex(GridViewColumn column, int newIndex)
{
var movedByUser = Items.First(col => col.GridViewColumn == column);
var placeTaken = Items.First(col => col.Index == newIndex);
placeTaken.Index = movedByUser.Index;
movedByUser.Index = newIndex;
movedByUser.Update();
placeTaken.Update();
//! iterate ListView rows
}
}
/// <summary>Represents a <see cref="System.Windows.Controls.GridViewColumn"/> and its index.</summary>
class ColumnIndex
{
public GridViewColumn GridViewColumn { get; set; }
public int Index { get; set; }
public void Update()
{
KeyboardNavigation.SetTabIndex(GridViewColumn, Index);
}
public override string ToString()
{
return string.Format("{0} : {1}", Index, GridViewColumn);
}
}
}
}