Binding ObservableCollection index - c#

I using json web server to retrieve data and put it into ObservableCollection that I bind it into my xaml, so i want to show index like
xxxx
xxxxxxx
How i can get number 1, 2 and so on?

If you are using DataGrid then in that case you need to enable DisplayRowNumber property and in the LoadingRow event of DataGrid you can set Row.Header with the index property. Code can be like
<DataGrid Name="dataGrid" LoadingRow="OnLoadingRow" behaviors:DataGridBehavior.DisplayRowNumber="True" ItemsSource="{Your Binding}" />
void OnLoadingRow(object sender, DataGridRowEventArgs e)
{
e.Row.Header = (e.Row.GetIndex() + 1).ToString();
}
Edit: As you want this for ListBox, so I suggest you to please check this solution. In this user is creating Index field and binding that with the ListBox.
Index = myCollection.ToList().IndexOf(e)
Also you can check Hannes blog post as well. He is showing example for Silverlight but it will work with WPF also.

You can do that using IMultiValueConverter which will return index.
XAML
<ListBox x:Name="listBox" ItemsSource="{Binding YourCollection}">
<ListBox.Resources>
<local:RowIndexConverter x:Key="RowIndexConverter"/>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource RowIndexConverter}">
<Binding/>
<Binding ElementName="listBox" Path="ItemsSource"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Converter:
public class RowIndexConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
IList list = (IList)values[1];
return list.IndexOf(values[0]).ToString();
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

Related

WPF Binding to a function return

I have a "Checked ListBox", a ListBox with a CheckBox for each of my Items.
In this ListBox I have a List of Players.
<ListBox ItemsSource="{Binding MyPlayers}" SelectedItem="{Binding SelectedPlayer}" Margin="69,51,347.4,161.8">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem>
<CheckBox IsChecked="{Binding ???}" Content="{Binding Path=Pseudo}" />
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
ViewModel :
public class MainWindowViewModel : BaseViewModel
{
SavingContext MyDatabaseContext;
public ObservableCollection<Player> MyPlayers
{
get
{
return MyDatabaseContext.MyPlayers.Local;
}
}
Tournament _MyTournament;
public Tournament MyTournament
{
get
{
return _MyTournament;
}
set
{
_MyTournament = value;
}
}
public MainWindowViewModel(Tournament myTournament)
{
MyDatabaseContext = new SavingContext();
MyDatabaseContext.MyPlayers.Load();
MyTournament = myTournament;
}
}
I pass in my ViewModel a Tournament, that contains an HashSet of Players called Participants.
I would like to bind my IsChecked properties to the result of MyTournament.Participants.Contains(this) with this being the Player related to the CheckBox. But I can't manage to make it works.
Edit :
I tried to use a Converter, but with no success.
<helper:PlayerToTournamentRegistered x:Key="PlayerToTournamentRegistered" />
<ListBoxItem>
<CheckBox IsChecked="{Binding Converter={StaticResource PlayerToTournamentRegistered}}" Content="{Binding Path=Pseudo}" />
</ListBoxItem>
public class PlayerToTournamentRegistered : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
//Temp test to see if it would work
if (value == null) return false;
return true;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
I get an error each time Provide value on 'System.Windows.Data.Binding' threw an exception
Any Advices ?
So I managed to get it working using a MultiConverter.
The first error that I had was due to the fact that the Mode is by Default Two Way, I only want to have my Converter work OneWay.
I used a MultiBinding to send to my Converter both my ListBoxItem and my Tournament.
My Converter return true or false, and is linked to my CheckBox.IsChecked properties.
List Box :
<ListBox ItemsSource="{Binding MyPlayers}" SelectedItem="{Binding SelectedPlayer}" Margin="69,51,347.4,161.8">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem>
<CheckBox Content="{Binding Path=Pseudo}" >
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource PlayerToTournamentRegistered}" Mode="OneWay">
<Binding />
<Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType=Window}" Path="DataContext.MyTournament"/>
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Converter :
public class PlayerToTournamentRegistered : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if(!(values[0] is Student) || !(values[1] is Tournament))
{
return false;
}
Tournament myTournament = (Tournament)values[1];
Student myPlayer = (Student)values[0];
if (myTournament.Participants.Contains(myPlayer))
return true;
return false;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}

File path to file name String converter not working

Using a wpf ListBox I'm trying to display a list of filename without displaying the full path (more convenient for user).
Data comes from an ObservableCollection which is filled using Dialog.
private ObservableCollection<string> _VidFileDisplay = new ObservableCollection<string>(new[] {""});
public ObservableCollection<string> VidFileDisplay
{
get { return _VidFileDisplay; }
set { _VidFileDisplay = value; }
}
In the end I want to select some items and get back the full file path. For this I have a converter :
public class PathToFilenameConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//return Path.GetFileName(value.ToString());
string result = null;
if (value != null)
{
var path = value.ToString();
if (string.IsNullOrWhiteSpace(path) == false)
result = Path.GetFileName(path);
}
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
Which I bind to my listbox itemsource :
<ListBox x:Name="VideoFileList" Margin="0" Grid.Row="1" Grid.RowSpan="5" Template="{DynamicResource BaseListBoxControlStyle}" ItemContainerStyle="{DynamicResource BaseListBoxItemStyle}" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ItemsSource="{Binding Path=DataContext.VidFileDisplay, Converter={StaticResource PathToFileName},ElementName=Ch_Parameters, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Path=SelectedVidNames,ElementName=Ch_Parameters, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
Without the converter, it is working fine (but of course this is the full path displayed in the listbox). With the converter I have one character per line... displaying this :
System.Collections.ObjectModel.ObservableCollection`1[System.String]
Where am I wrong ?
Thank you
In ItemsSource binding converter applies to the whole list and not to each item in the collection. If you want to apply your converter per item you need to do it ItemTemplate
<ListBox x:Name="VideoFileList" ItemsSource="{Binding Path=DataContext.VidFileDisplay, ElementName=Ch_Parameters}" ...>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=., Converter={StaticResource PathToFileName}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

WPF - binding to a property in ViewModel and to other control

In my WPF application (using MVVM) I have a CheckBox and a TextBlock. When the CheckBox is checked the value from the TextBlock will be saved. There is a binding from both controls to my ViewModel. Below simplified XAML:
<StackPanel>
<Label>Add to list</Label>
<CheckBox IsChecked="{Binding Path=AddItem}"></CheckBox>
<Label>Gross amount:</Label>
<TextBlock Text="{Binding Path=Amount}"></TextBlock>
</StackPanel>
Now I would like to have the CheckBox checked when a user starts typing in the TextBlock. I know binding can do that but I already bind to a property in my ViewModel. How can I bind to a property in ViewModel and to other control?
You should use the multibinding. Something like this:
<CheckBox Content="CheckBox" HorizontalAlignment="Left" Margin="191,82,0,0" VerticalAlignment="Top">
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource checkConverter}">
<Binding Path="IsChecked"/>
<Binding Path="UserStartedTyping"/>
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
checkConverter is a MultiValueConverter that you need in order to decide what to do with the values you are binding with (such as &&, || etc.).
public class CheckConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return (bool)((bool)values[0] || (bool)values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
object[] splitValues = { value, false };
return splitValues;
}
}
UserStartedTyping is a property in the ViewModel that would be set to true when KeyDown event is fired.
Hope it helps.
You can try setting the AddItem to true when the user starts to change the amount value:
private string _amt;
public string Amount
{
get{return _amt;}
set
{
_amt = value;
if(AddItem == false)
AddItem = true;
//PropertyChanges here
}
}

Binding inside of a binding

Ok I have a Observable collection containing string defined like so.
public ObservableCollection<string> OCGroundType { get; set; }
This collection is having resources key so what I'm trying to do is with this code
<ListBox HorizontalAlignment="Left" Height="110" Margin="156,23,0,0" VerticalAlignment="Top" Width="314" ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=OCGroundType}" >
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Path=, Source={StaticResource Resources}}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
So what I'm trying to do is give the path the value of the itemsource is that possible?
Edit
That's what the staticresource is 'binded' to , a resourceDictionary
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cultures="clr-namespace:Marcam.Cultures"
xmlns:properties="clr-namespace:Marcam.Properties">
<ObjectDataProvider x:Key="Resources" ObjectType="{x:Type cultures:CultureResources}" MethodName="GetResourceInstance"/>
</ResourceDictionary>
What the ObjectType is 'binded' to is a method who return the current Resources.resx, If I've understanded well how it's work because I've based this code on this WPF Localization
It's not possible, because a Binding is not a DependencyObject, so its properties cannot be bound.
However, you could achieve the desired result by using a converter that fetches the appropriate value from the resources.
EDIT: first, you need a converter like this:
public class ResourceConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string key = (string)value;
return Properties.Resources.ResourceManager.GetObject(key, culture);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
Then, declare an instance of this converter in the XAML resources:
<local:ResourceConverter x:Key="resourceConverter" />
And finally, just use this converter in your binding:
<Label Content="{Binding Path=., Converter={StaticResource resourceConverter}}"/>
Assuming Resources are mere string values declared under resources section of Window or UserControl, you can achieve that using IMultiValueConverter.
First of all you need to pass resource key and second ResourceDictionary of Window/UserControl wherever your resources are defined.
XAML will look like this:
<Label>
<Label.Content>
<MultiBinding Converter="{StaticResource ResourceToValueConverter}">
<Binding/>
<Binding Path="Resources" RelativeSource="{RelativeSource
Mode=FindAncestor, AncestorType=Window}"/>
</MultiBinding>
</Label.Content>
</Label>
Ofcourse you need to add instance of ResourceToValueConverter in XAML.
Second here goes your converter code:
public class ResourceToValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
return ((ResourceDictionary)values[1])[values[0]];
}
public object[] ConvertBack(object value, Type[] targetTypes,
object parameter,
System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
UPDATE
As you edited the question to include that resource is ObjectDataProvider, you can modify converter code.
You have ObjectDataProvider in your converter which you can get and call it's method to get the resource file(.resx) file. Get string value from the resource file and return it.
var provider = ((ResourceDictionary)values[1])["Resources"] as ObjectDataProvider;
var output = provider.Data;
return // Get string from resource file and return it.

Complex MultiBinding validation

I have a form that has multiple fields. I have also a "Validate" button that will action a DB input. I would like that button to be activated only if the bare minimum fields are defined by the user.
So far it was quite simple as all the fields were text:
<Button x:Name="Manage" Content="Manage">
<Button.IsEnabled>
<MultiBinding Mode="OneWay" Converter="{StaticResource FieldsFilledinToVisible}">
<Binding ElementName="name1" Path="Text"/>
<Binding ElementName="name2" Path="Text"/>
</MultiBinding>
</Button.IsEnabled>
</Button>
Converter being:
public class AllValuesDefinedConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
bool isEnabled = false;
for (int i = 0; i < values.Length; i++)
{
isEnabled = isEnabled || string.IsNullOrEmpty(values[i].ToString());
}
return !isEnabled;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
return null;
}
}
But would now have to consider extra check boxes, where the condition should be [Any of these checkboxes checked + previous text fields defined --> Activate the validation button]:
<WrapPanel Style="{StaticResource WrapStyle_Inputs}">
<CheckBox Content="Check1" IsChecked="{Binding Checked1, Mode=TwoWay}"/>
<CheckBox Content="Check2" IsChecked="{Binding Checked2, Mode=TwoWay}"/>
<CheckBox Content="Check3" IsChecked="{Binding Checked3, Mode=TwoWay}"/>
</WrapPanel>
Would you know how I could do so?
thank you!
Converters are the wrong tool for the job. You should instead look at validation, commands, and view models. Your view model would implement validation logic and expose a command that your button is bound to. The command would only be executable if your validation logic passes.

Categories

Resources