Unable to retrieve actual Width - wpf

I am having one list view
<ListView Grid.Row="4" Grid.ColumnSpan="2"
Margin="{Binding ElementName=BorderContainer, Path=Margin}"
FontSize="{DynamicResource PlaceHolderFontSize}"
ItemContainerStyle="{StaticResource ListItemContainerStyle}"
Foreground="Black"
>
<ListView.View>
<GridView ColumnHeaderContainerStyle="{StaticResource BorderlessHeader}">
<GridViewColumn Header="Last Backup Date/Time" HeaderTemplate="{StaticResource BlueHeader}"
Width="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}, Converter={StaticResource ColumnWidth},ConverterParameter=221}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Date}" ToolTip="{Binding Date}" FontSize="{DynamicResource ContentFontSize}" Style="{StaticResource GridColumText}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
As mentioned in the code am calculating the header width using converter. and inside my converter am using like below
public object Convert(object value, Type target Type, object parameter, Culture Info culture)
{
ListView listview = value as ListView;
double width = listview.ActualWidth;
but the listview.actualwidth is always giving "0" as result. Why this is happening? any ideas?

Why this is happening?
Probably because the ListView actually has an actual width of 0 by the time the Convert method gets invoked.
any ideas?
You could use a MultiBinding and an IMultiValueConverter that binds to both the ListView itself and its ActualWidth property:
<GridViewColumn Header="Last Backup Date/Time" HeaderTemplate="{StaticResource BlueHeader}">
<GridViewColumn.Width>
<MultiBinding Converter="{StaticResource ColumnWidth}" ConverterParameter="221">
<Binding Path="." RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ListView}" />
<Binding Path="ActualWidth" RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=ListView}" />
</MultiBinding>
</GridViewColumn.Width>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Date}" ToolTip="{Binding Date}" FontSize="{DynamicResource ContentFontSize}" Style="{StaticResource GridColumText}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
public class ColumnWidthConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
ListView listview = values[0] as ListView;
double width = listview.ActualWidth;
if (width == 0)
return (double)parameter;
//...
return 100.0;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Then the converter should be invoked whenever the ActualWidth property is updated.

If you have for example 4 Columns, you can try the following:
XAML
<ListView x:Name="listView">
<ListView.View>
<GridView>
<GridViewColumn Width="{Binding ElementName=listView, Path=ActualWidth, Converter={StaticResource ColumnWidthConverter},ConverterParameter=4}">
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Converter
public class ColumnWidthConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
int columns;
if (int.TryParse(parameter.ToString(), out columns))
{
double width;
if (columns > 0 && double.TryParse(value.ToString(), out width))
{
return width / columns;
}
}
return 100.0;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
If you want to be more dynamic, you can write a Behavior and attach it to the ListView. There you can dynamic calculate the values based on the Columns count.

Related

How do I obtain the Index of a ListViewItem?

I have a list view that uses a collectionsource and wish to set the background colour of the ListViewItems depending upon whether their index in the list matches a value held in the view model. To do this I am binding the background of the listVeiwItem to the value in the ViewModel, and using a converter to determine the background colour. To make this determination the converter needs to be passed the ListViewItem's index. How do I obtain the index using XAML?
Here is the XAML for the data template used by the ListView:
<DataTemplate x:Key="ILMemberTemplate">
<StackPanel Orientation="Horizontal" Background="{Binding Path=ListIndex, Mode=OneWay, Converter={StaticResource ParticipantBackground}, ConverterParameter={???}}">
<TextBlock
Width="200"
TextAlignment="Left"
Foreground="{Binding Path=IsPC, Mode=OneWay, Converter={StaticResource ParticipantColour}}"
Text="{Binding Path=Name, Mode=OneWay}"/>
<TextBlock
Width="40"
TextAlignment="Center"
Foreground="{Binding Path=IsPC, Mode=OneWay, Converter={StaticResource ParticipantColour}}"
Text="{Binding Path=Initiative, Mode=OneWay}"/>
</StackPanel>
</DataTemplate>
You can't obtain the index in XAML and pass it as a ConverterParameter. This is not possible. But you can use a multi converter and bind to both the ListIndex and the parent ListViewItem container, e.g.:
class MultiConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var listIndex = values[0];
//...
ListViewItem item = value as ListViewItem;
ListView lv = FindParent<ListView>(item);
ICollectionView view = lv.ItemsSource as ICollectionView;
IEnumerator e = view.GetEnumerator();
int index = 0;
while (e.MoveNext())
{
if (Equals(item.DataContext, e.Current))
return index;
else
index++;
}
//return some brush based on the indexes..
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
private static T FindParent<T>(DependencyObject dependencyObject) where T : DependencyObject
{
var parent = VisualTreeHelper.GetParent(dependencyObject);
if (parent == null) return null;
var parentT = parent as T;
return parentT ?? FindParent<T>(parent);
}
}
XAML:
<DataTemplate x:Key="ILMemberTemplate">
<StackPanel Orientation="Horizontal">
<StackPanel.Background>
<MultiBinding Converter="{StaticResource multiConverter}">
<Binding Path="ListIndex" />
<Binding Path="." RelativeSource="{RelativeSource AncestorType=ListViewItem}" />
</MultiBinding>
</StackPanel.Background>
<!-- ... -->
</StackPanel>
</DataTemplate>

WPFLocalizationExtension with ItemTemplate

I'm using WPFLocalizationExtension for my WPF app.
I have one ComboBox for languages selection. Item source is an ObservableCollection<KeyValuePair<string, string>> as below:
TITLE_LANGUAGE_ENGLISH : en
TITLE_LANGUAGE_VIETNAMESE: vi-VN
This is my xaml code:
<TextBlock Text="{lex:Loc TITLE_LANGUAGE}"></TextBlock>
<ComboBox Grid.Column="1"
ItemsSource="{Binding AvailableLanguages}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{lex:Loc Key={Binding Key}}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
When I run the application, it throws me an exeption as below:
A 'Binding' cannot be set on the 'Key' property of type 'LocExtension'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject
How can I translate the ItemTemplate ?
Thank you,
You could use an IMultiValueConverter together with a MultiBinding, so that you don't loose the ability to update the localization on-the-fly.
<ComboBox ItemsSource="{Binding AvailableLanguages}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding>
<MultiBinding.Bindings>
<Binding Path="Key" Mode="OneTime"/>
<Binding Path="Culture" Source="{x:Static lex:LocalizeDictionary.Instance}"/>
</MultiBinding.Bindings>
<MultiBinding.Converter>
<l:TranslateMultiConverter/>
</MultiBinding.Converter>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
And here is the converter:
class TranslateMultiConverter : DependencyObject, IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length == 2)
{
var key = values[0] as string;
if (key == null)
{
return DependencyProperty.UnsetValue;
}
var cultureInfo = (values[1] as CultureInfo) ?? culture;
return LocalizeDictionary.Instance.GetLocalizedObject(key, this, cultureInfo);
}
return values.FirstOrDefault();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
The LocalizeDictionary will raise a PropertyChanged event when the app's language will change causing the MultiBinding to refresh the values.
Note that the converter is a DependencyObject too. This is to provide the context to the LocalizeDictionary when calling the GetLocalizedObject method.
you have to bind to the Path Key directly. The TextBlock at DataTemplate points directly to a single KeyValuePair object, that you can access the property Key directly.
<TextBlock Text="{lex:Loc TITLE_LANGUAGE}"></TextBlock>
<ComboBox Grid.Column="1"
ItemsSource="{Binding AvailableLanguages}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Key}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Update:
Maybe you have to add a Converter. Try WPFLocalizeExtension.TypeConverters.DefaultConverter or implement a class deriving from IValueConverter by yourself.
<ComboBox.Resources>
<cv:DefaultConverter x:Key="DConv" />
</ComboBox.Resources>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Key, Converter={StaticResource DConv}}" />
</DataTemplate>
</ComboBox.ItemTemplate>

Binding all Texboxes in datagrid to list of string

i would like binding all textboxes from datagrid to list of string. I do not know how to do it.
I have textboxes in one column of datagrid:
<DataGrid ItemsSource="{Binding Data}">
<DataGridTextColumn Binding="{Binding Title}" Header="Title" IsReadOnly="True">
DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Width="60" Text="{Binding DataList, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"></TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGridTextColumn>
</DataGrid>
In ViewModel:
public ObservableCollection<DataObject> Data //datasource
{
get { return _data;}
set { _data = value; OnPropertyChanged(nameof(Data)); }
}
public ObservableCollection<string> DataList //here must be data from textboxes
{
get { return _dataList; }
set { _dataList = value; OnPropertyChanged(nameof(DataList)); }
}
For simplicity I did not write here call of command. In action method of command is DataList property still empty.
Thanks
Try this :
<DataGrid ItemsSource="{Binding Data}" AutoGenerateColumns="False">
<DataGrid.Resources>
<local:CollectionToStringConverter x:Key="CollectionToStringConverter"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Title" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding DataContext.DataList,Converter={StaticResource CollectionToStringConverter},RelativeSource={RelativeSource AncestorType={x:Type DataGrid}}, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Write converter for converting collection of strings to one single string.
[ValueConversion(typeof(List<string>), typeof(string))]
public class CollectionToStringConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (targetType != typeof(string))
throw new InvalidOperationException("The target must be a String");
return String.Join(", ", ((IList<string>)value).ToArray());
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
Thanks to WPF textblock binding with List

How do I Bind 2 controls to 1 field AND also access the 2 control values for ConvertBack?

In a DataGridTemplateColumn DataTemplate,
I want to bind 2 controls to a string field of format "[name]:[value]" i.e. the string is delimited by colon ":". I need to bind control a) to the [name] part and control b) the value part.
I have been able to successfully use an IValueConverter to split the string for display:
public class NameAndValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string rtn = "";
string[] split = value.ToString().Split(':');
if (split.Count() == 2)
{
if(parameter.ToString() == "Name")
rtn = split[0];
if(parameter.ToString() == "Value")
rtn = split[1];
}
return rtn;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException("NameAndValueConverter can only be used OneWay.");
}
}
And the XAML:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<local:NameAndValueConverter x:Key="NameAndValueConverter" />
</StackPanel.Resources>
<TextBox x:Name="namePart" Text="{Binding Path=FieldType, Converter={StaticResource NameAndValueConverter}, ConverterParameter='Name'}" />
<TextBox x:Name="valuePart" Text="{Binding Path=FieldType, Converter={StaticResource NameAndValueConverter}, ConverterParameter='Value'}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
But the data may be edited in the Textboxes so how can I access the 2 TextBox values in ConvertBack so that they can be joined again?
Doing this in the XAML:
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<local:NameAndValueConverter x:Key="NameAndValueConverter" />
</StackPanel.Resources>
<TextBox x:Name="namePart" Text="{Binding Path=FieldType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource NameAndValueConverter}, ConverterParameter='Name'}" />
<TextBox x:Name="valuePart" Text="{Binding Path=FieldType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource NameAndValueConverter}, ConverterParameter='Value'}" />
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
should be enough. You need a TwoWay binding to edit from UI, and when you set UpdateSourceTrigger to PropertyChanged the TextBox will update itself automatically when the property will be modified in the ViewModel (you'd obviously need to implement INotifyPropertyChanged )

how to edit listview row with a combo

I have a ListView with rows on it. When I click on a particular cell, I want that selected cell to be editable with a combo box and the thing is I have done it, but the combox box still remains even after editing. I want the combo box to change back to textblock.
<Style TargetType="{x:Type FrameworkElement}"
x:Key="GridEditStyle">
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Visibility"
Value="{Binding Path=IsSelected,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ListViewItem}},
Converter={StaticResource boolToVis},
ConverterParameter=True}" />
</Style>
<ComboBox SelectedItem="Present"
ItemsSource="{Binding ElementName=This,
Path=AvailablePublishers}"
Style="{StaticResource GridEditStyle}" />
code behind is
private ObservableCollection<string> _AvailablePublishers =
new ObservableCollection<string>();
public Student_Attendance()
{
InitializeComponent();
_AvailablePublishers.Add("Present");
_AvailablePublishers.Add("Absent");
_AvailablePublishers.Add("Late");
}
public ObservableCollection<string> AvailablePublishers
{ get { return _AvailablePublishers; } }
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
bool param = bool.Parse(parameter as string);
bool val = (bool)value;
return val == param ? Visibility.Visible : Visibility.Hidden;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
plz help me out
thank you
You know if you used a DataGrid, you would't have to do anything for switching the viewing and editing templates, it's done automatically ... here's a sample to get you started:
<DataGrid ItemsSource="{Binding ...}">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock DataContext="{Binding ...}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding ElementName=This, Path=AvailablePublishers}" SelectedItem="{Binding ...}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>

Resources