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

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
}
}

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();
}
}

Binding items in ListBox in WP8

In a Windows Phone 8 app,
I have a listbox with 2 TextBlocks and a button.
I have a list of 2 strings and a boolean & I am able to bind the strings to the TextBlocks.
<ListBox Name="ListboxTest">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}" TextWrapping="Wrap"/>
<TextBlock Text="{Binding Value}" TextWrapping="Wrap"/>
<Button />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
And this is the C# code to bind to the list box.
public class Detail
{
public string Key { get; set; }
public string Value { get; set; }
public bool check { get; set; }
}
public List<Detail> ClassList = new List<Detail>();
ListboxTest.ItemsSource = ClassList;
I want to display the button only when the boolean value is true.
How do I do it?
Take a look at this. Actually what you really need is a Converter by implementing the IValueConverter. This is also a good example where you could read about it. Bind the boolean value with the visibility property of the button and you are done! ;)
You can use boolean to visibility converter to hide, show button
Here are example:
public class BoolToVisibility : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var boolValue = false;
if (value != null) boolValue = (bool)value;
return boolValue ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
In app.xaml
<my:BoolToVisibility x:Key="BoolToVisibility"/>
In your data template
<Button Visibility="{Binding Path=YourBoolProperty,Converter={StaticResource BoolToVisibility}}>
Or, you could add this property to the Detail class:
public Visibility ButtonVisibility {
get {
return this.check == true ? Visibility.Visible : Visibility.Collapsed;
}
}
And then just bind the button's Visibility to the ButtonVisibility property without any converters.
<Button Visibility="{Binding ButtonVisibility}" />
Please try those use Triggers.
Various Triggers in windows phone Msdn.
Please use ObservableCollection in WP8 for binding instead of List.
Please make your properties are implemented with INotifyPropertyChanged If your Boolean property is not implemented with inotifypropertychanged the view will not know the value is changed.hence the Data trigger will not work.
Namespace
xmlns:interactivity="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ec="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
x:Class="XXX_XXXX"
<Button Content="My button"
Stretch="None"
HorizontalAlignment="Stretch"
VerticalAlignment="Top">
<interactivity:Interaction.Triggers>
<ec:DataTrigger Binding="{Binding Check}" Value="True">
<ec:ChangePropertyAction PropertyName="Visibility">
<ec:ChangePropertyAction.Value>
<Visibility>Visible</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</ec:DataTrigger>
<ec:DataTrigger Binding="{Binding Check}" Value="False">
<ec:ChangePropertyAction PropertyName="Visibility">
<ec:ChangePropertyAction.Value>
<Visibility>Collapsed</Visibility>
</ec:ChangePropertyAction.Value>
</ec:ChangePropertyAction>
</ec:DataTrigger>
</interactivity:Interaction.Triggers>
</Button>
Note Answered from phone syntax may not be correct

Binding ObservableCollection index

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();
}
}

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.

Conditional element in xaml depending on the binding content

Is it possible to display this TextBlock, only if the Address.Length > 0 ? I'd like to do this directly into the xaml, I know I could put all my controls programmatically
<TextBlock Text="{Binding Path=Address}" />
Basically, you're going to need to write an IValueConverter so that you can bind the Visibility property of your TextBox to either the Address field, or a new field that you create.
If you bind to the Address field, here's how the binding might look like::
<TextBlock Text="{Binding Path=Address}"
Visibility="{Binding Path=Address, Converter={StaticResource StringLengthVisibilityConverter}}" />
And then StringLengthVisiblityConverter could look something like this:
public class StringLengthVisiblityConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || value.ToString().Length == 0)
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// Don't need to implement this
}
}
Then you'd just need to add your converter as a resource, using syntax like this (where src is mapped to the namespace where the converter is defined):
<src:StringLengthVisiblityConverter x:Key="StringLengthVisiblityConverter" />
I would do this with another boolean property called HasAddress which returns Address.Length > 0.
<!-- In some resources section -->
<BooleanToVisibilityConverter x:Key="Bool2VisibilityConverter" />
<TextBlock
Text="{Binding Address}"
Visibility="{Binding HasAddress, Converter={StaticResource Bool2VisibilityConverter}}"
/>
You should also remember to notify the property change for HasAddress in the setter of Address.
You can create a StringToVisibility converter.
It will return Visibility.Visible if bound string is not null or empty and Visibility.Collapsed if it is.
Use this converter while binding Address with Visibility property of your TextBlock.
Example:
<TextBlock Text="{Binding Path=Address}" Visibility="{Binding Address, Converter={StaticResource StringToVisibilityConverter}}" />

Categories

Resources