WPF - Two way binding - c#

I've created user control (toggle button) that contains property called ButtonState (Checked, Disabled or Available). Control has built-in mouse click event which converts its state from available to pressed or from pressed to available (if it is disabled it can't be clicked).
My purpose is create "tree" of buttons, something like that:
OPTION 1 OPTION 2
SUBOPTION1 SUBOPTION1 SUBOPTION1 SUBOPTION1
.... .....
So if I click on button "OPTION 1" (it changes state to PRESSED within control) I would like to button "OPTION 2" go to state DISABLED. If I click on OPTION 1 again, it converts from PRESSED to AVAILABLE and OPTION2 goes to AVAILABLE TOO. The same procedure should be run if I click on OPTION2 (analogously of course). Briefly again: only one button can have PRESSED state and if one has such state other one must be disabled. And if one is available - the other must be available as well.
I've created converter (InverseButtonStateConverter) BUTTONSTATE -> BUTTONSTATE
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
switch (((Controls.ButtonState)value))
{
case Controls.ButtonState.Available: return Controls.ButtonState.Available;
case Controls.ButtonState.Pressed: return Controls.ButtonState.Disable;
case Controls.ButtonState.Disable: return Controls.ButtonState.Available;
default: return Controls.ButtonState.Available;
}
}
Using in XAML :
<Controls:ToggleRectangleButton HorizontalAlignment="Center" VerticalAlignment="Center" Height="109" Width="210" ButtonText2="Bilety jednorazowe" TextFontSize="25" Grid.Column="0" x:Name="btSingleTicket" Click="btSingleTicket_Click" ButtonState="{Binding ElementName=btTimeTicket, Path=ButtonState, Converter={StaticResource InverseButtonStateConverter}}"/>
<Controls:ToggleRectangleButton HorizontalAlignment="Center" VerticalAlignment="Center" Height="109" Width="210" ButtonText2="Bilety czasowe" TextFontSize="25" Grid.Column="3" x:Name="btTimeTicket" Click="btTimeTicket_Click" ButtonState="{Binding ElementName=btSingleTicket, Path=ButtonState, Converter={StaticResource InverseButtonStateConverter}}"/>
When I run this code it works good when I'm clicking on one button. If I interrupt it by clicking on second (it won't work as I would like to) first's button ability to correctyly working disappear.
I've tried to change binding modes but I have no idea how to make it. Some help?

Using two ToggleButtons, and a boolean inverter converter, you can achieve it by binding their respective IsEnabled properties to the IsChecked properties:
<ToggleButton Content="1" x:Name="btn1" IsEnabled="{Binding ElementName=btn2, Path=IsChecked, Converter={StaticResource BooleanInverterConverter}}"/>
<ToggleButton Content="2" x:Name="btn2" IsEnabled="{Binding ElementName=btn1, Path=IsChecked, Converter={StaticResource BooleanInverterConverter}}"/>
And the ValueConverter (simple implementation):
public class BooleanInverterConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return !(bool) value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Related

Is there any way to check for a condition in XAML

I have a Datagrid in which data comes from Database based on some selection and therefore the values keep on changing at different selections.
On the basis of the data, I want to add a TextBox to the UI?
This is .NET framework 4.5
<DataGrid Name="Sampledatagrid" AutoGenerateColumns="True" ItemsSource="`{StaticResource MyCustomers}" Margin="0,0,0,106"/>
<Canvas>
<br/>
<!-- if Sampledatagrid.Value == 'Adam' -->
<br/>
<br/>
<TextBox Canvas.Left="135" Canvas.Top="12" Style={StaticResource textboxstyle} />
<br/>
<br/>
</Canvas>
When the Sampledatagrid contains "Adam" it should display a textbox.
First, to let your TextBox show the selected entry of the DataGrid, set its Text-Binding to the corresponding value.
Just add the 'Text' property in the correct way:
<TextBox Text="{Binding ElementName=Sampledatagrid, Path=SelectedItem.Name}" Canvas.Left="135" Canvas.Top="12">
Possibly, the name of your variable "SelectedItem.Name" is another one than "Name".
Second, if the TextBox shall only show 'valid' names (like e.g. 'Adam'), I show you how to use a converter to do so.
Create a new file in your project called 'ValidNamesConverter.cs'. This file gets following code:
using System;
using System.Globalization;
using System.Windows.Data;
namespace WpfApp1
{
internal class ValidNamesConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string valAsString = value.ToString();
if (valAsString == "Adam" || valAsString == "Eve")
return value;
return string.Empty;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
In the "Resources" section of your MainWindow.xaml (or UserControl.xaml) add an instance (a static resource) of this converter-class. (if you don't have already a "Resources" section, just add one):
<Window.Resources>
<!-- make a XAML instance of the class ValidNamesConverter -->
<local:ValidNamesConverter x:Key="validNamesConverter" />
</Window.Resources>
Then, extend the TextBox's Text-Binding to use this converter.
<TextBox Text="{Binding ElementName=Sampledatagrid, Path=SelectedItem.Name, Converter={StaticResource validNamesConverter}}" Canvas.Left="135" Canvas.Top="42">
The converter in this case decides, which string shall be 'forwarded' to the TextBox. So, if a valid name is given into the converter, it will just return that name again. If an invalid name is given, the converter returns an empty string.
Anyway, in my example, the TextBox is always visibly shown. If you want the TextBox to be absent when an invalid name is selected, you probably need a second converter (e.g. ValidNameToVisibilityConverter) which you bind to the TextBox's Visibility-property. That converter then returns either Visibility.Visible or Visibility.Collapsed. Within this 2nd converter you can make use of the ValidNamesConverter to avoid having a set of valid names twice in your code.

C# WPF Image Converter for Buttons based on Boolean Value

I am making an RPG in WPF and C#. I have movement buttons with images attached. I am trying to figure out how to change the image of the button depending on if there is a room available to move to in that direction. I have looked up converters but I am not quite sure how to implement them for my situation.
This is one example I have tried to implement that I found online:
<Button Content="{Binding MyBooleanValue, Converter={StaticResource
MyBooleanToImageConverter}}" />
public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
bool v = (bool)value;
Uri path = new Uri((v ? "ImgSrcIfTrue.png" : "ImgSrcIfFalse.png"), UriKind.Relative);
return new Image()
{
Source = new System.Windows.Media.Imaging.BitmapImage(path),
Height = ...,
Width = ...,
};
}
Here is part of the code I am working on
<!-- Movement Buttons -->
<Button Grid.Row="1" Grid.Column="1"
Click="OnClick_MoveNorth">
<StackPanel>
<Image Source= "/Image/Buttons/Up.png"/>
</StackPanel>
</Button>
I already have functions for the boolean values, i am just trying to figure out how to implement a Converter to change the button image.
I have used the Boolean Visibility and hoping to do something similar.
Visibility="{Binding HasMonster, Converter={StaticResource BooleanToVisibility}}"
Better bind the Source property of an Image element in the Content of the Button:
<Button>
<Image Source="{Binding MyBooleanValue,
Converter={StaticResource MyBooleanToImageConverter}}"/>
</Button>
The converter would directly return a BitmapImage. If the image files are supposed to be assembly resources (i.e. they are part of your Visual Studio project and their Build Action is set to Resource), they must be loaded from Pack URIs:
public class BooleanToImageConverter : IValueConverter
{
public object Convert(
object value, Type targetType, object parameter, CultureInfo culture)
{
var uri = (bool)value
? "pack://application:,,,/ImgSrcIfTrue.png"
: "pack://application:,,,/ImgSrcIfFalse.png";
return new BitmapImage(new Uri(uri));
}
public object ConvertBack(
object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
You would add the converter to the Window's Resources like this:
<Window.Resources>
<local:BooleanToImageConverter x:Key="MyBooleanToImageConverter"/>
...
</Window.Resources>

How to check or uncheck checkboxes inside of a List Box WPF

I have a combobox which data source comes from a table in my DataBase. So, each item in my combo is an Object from the table. This Object have an attribute which corresponds to a string full of "1"s or "0"s. On the other hand I have a list of checkboxes inside of a ListBox with this template:
<ListBox Height="150" MinHeight="100" HorizontalAlignment="Center" Name="lstEstudios" VerticalAlignment="Top" Width="200"
ItemsSource="{Binding}" SelectionMode="Multiple" Margin="0,20,0,0">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Name="chkEstudios" Width="Auto" Content="{Binding Path=Nom_estudio}"
Checked="chkEstudios_Checked" Unchecked="chkEstudios_Unchecked"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I don´t know if it's possible but, that I want to do is, for each "1" or "0" in the attribute set the checkbox checked or unchecked depending if there is a "1" check the checkbox or if is "0" uncheck the checkbox, and so on... with all the checkboxes in the ListBox, how to do that ?
I tried the same thing with my own sample having a CustomTask class.
<ListBox ItemsSource="{Binding CustomTasks}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding TaskStatus, Converter={x:Static testApp:StatusToBooleanConverter.Instance}}" Content="{Binding TaskStatus}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
where the TaskStatus is a boolean of two values, i.e Completed and Pending.
and here is the code for the converter
public class StatusToBooleanConverter : IValueConverter
{
public static StatusToBooleanConverter Instance = new StatusToBooleanConverter();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Status)
{
switch ((Status)value)
{
case Status.Completed:
return true;
case Status.Pending:
return false;
}
return null;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Just try this out. Hope that helps.
This is a good place to use a converter. If you have a property with 1's and 0's in it and you want to translate those to true/false for the checked attribute then try making converters to do the work. The basic idea behind a converter is to take an input value (from a bound property) and, as the name implies, convert it to a different value. You can make them as simple or complicated as you would like, and turning "1" into true and "0" into false should be pretty quick.
You will bind the IsChecked attribute of the checkbox to your source of 1's and 0's and in the binding also use the converter.
If you haven't made a value converter before here is a nice tutorial on making one: http://wpftutorial.net/ValueConverters.html

Reverse bool value in binding

I have a TextBlock like this:
<TextBlock Visibility="{Binding IsOnline, Converter={StaticResource boolToVisibilityConverter}}">
boolToVisibility returns Visible if IsOnline is true. But in a situation I want textblock be Collapsed if IsOnline is true.
I can make another converter which acts in reverse, but I want to know isn't it possible to do that in XAML with current converter?
You could potentially use a ConverterParameter value to determine whether to invert the output, for instance:
<TextBlock Visibility="{Binding IsOnline, ConverterParameter=true, Converter={StaticResource boolToVisibilityConverter}}" />
And in the converter itself:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
bool invertOuput = false;
if (parameter != null) {
bool.TryParse((string)parameter, out invertOuput)
}
// TODO: Converter logic
}
As far as I'm concerned, you'll have to make another converter, although, your converter (not reverese) already exists:
http://msdn.microsoft.com/pl-pl/library/system.windows.controls.booleantovisibilityconverter(v=vs.110).aspx

WPF DataBinding does not update property (just only one time at startup)

I´m having some trouble binding some items attributes.
I have comboboxes and a buttons in a itemscontrol. The combobox is for searching Localities by name. Thats why when the combobox is created, the property IsEditable is true, in order to let the user enter a name, and then press left-control to search that string in the database via WCF.
Then, when the combobox ItemSource.Count is al least 1, I block the combobox by setting IsEditable = false (using the DataBinding of the button). Thats when the button have to change the visibility from hidden to visible, because pressing the button set IsEditable to true again, and ables the user to input a name to search.
To achieve this, I have binded the combobox IsEditable with the button Visibility attribute, and used the following converter, which works:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
public class VisibilityToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (Visibility)value == Visibility.Visible ? false : true;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
As I said, the left control button search the localities, for that I´m using the keydown event:
private void ComboBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.LeftCtrl)
{
ComboBox cbx = sender as ComboBox;
LocationServiceClient locationService = new LocationServiceClient();
if (cbx != null)
{
cbx.ItemsSource = locationService.SeachLocalities(new SearchLocalitiesRequest { Search = cbx.Text, MaxItems = 20 }).Localities;
cbx.DisplayMemberPath = "LocalityName";
localityCombobox = cbx;
cbx.IsDropDownOpen = true;
}
}
}
As the Items of the Combobox changed, wouldn´t that have to affect the binding of the Button visibility?
The binding uses this converter, which works too, but only executes once, when I run the app. Thats the problem I´m having, it just does not update the button visibility, and leaves it on Hidden:
public class ItemsSourceCountToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var b = (int)value > 0 ? Visibility.Visible : Visibility.Hidden;
return b;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
This the image of the control, it might help you to get what I say:
Just in case, this is the xaml i used:
<ComboBox Name ="cbxLocality" Width="200" DisplayMemberPath="{Binding LocalityName}" IsEditable="{Binding ElementName= btnRemoveLocality, Path=Visibility, Converter={StaticResource VisibilityToBooleanConverter}}" KeyDown="ComboBox_KeyDown">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding LocalityName}"/>
<TextBlock FontSize="10">
<Run Text="CP: "/>
<Run Text="{Binding ZipCode}"/>
<Run Text=" | "/>
<Run Text="{Binding Province.ProvinceName}"/>
</TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Name ="btnRemoveLocality" Content="x" Visibility="{Binding ElementName= cbxLocality, Path=Items.Count, Converter={StaticResource ItemsSourceCountToVisibilityConverter}}" Click="Button_Click_3"></Button>
Does it work to call .DataBind() on cbx when changing ItemSource?
Edit: I would bind the visibility for path Items.Count, instead of just Items, and make the converter handle the integer instead of the Item-list. Because the Count-property triggers the PropertyChanged-event, and the list itself will not if an element is added/removed.
Edit 2: Declare the ObservableCollection of you items as a public property outside the method itself, so it will have public scope. And set it as ItemsSource. Then you won't have to change the ItemSource-property, only the collection itself.

Categories