Combobox showing error "Binding expression path error" - c#

I have two combobox which are currently binded to the user model. The first combobox supposed to show the current userRole value before clicking on the combobox. The other combobox supposed to show the userStatus either 1 or 0. Now the second combobox is not displaying any value. However the first combobox is displaying the value once its clicked only.
Here is the xaml code:
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="12" Text="User Role: " VerticalAlignment="Center" />
<ComboBox x:Name="cbUserRole" FlowDirection="LeftToRight" FontSize="16" Foreground="MidnightBlue" HorizontalAlignment="Stretch" VerticalAlignment="Center" Loaded="cbUserRole_Loaded" SelectedItem="{Binding UserRole, Mode=TwoWay" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="12" Text="User Status: " VerticalAlignment="Center" />
<ComboBox x:Name="cbUserStatus" FlowDirection="LeftToRight" FontSize="16" Foreground="MidnightBlue" HorizontalAlignment="Stretch" VerticalAlignment="Center" SelectedIndex="{Binding UserStatus, Converter={StaticResource boolToIndexConverter}}" />
</StackPanel>
Here is my converter code:
public class BoolToIndexConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool)value == true) ? 0 : 1;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((int)value == 0) ? true : false;
}
}
The code above is refered from this link:
Here is my User model code:
private string userrole;
public string UserRole
{
get { return userrole; }
set
{
userrole = value;
OnPropertyChanged("UserRole");
}
}
private bool userstatus;
public bool UserStatus
{
get { return userstatus; }
set
{
userstatus = value;
OnPropertyChanged("UserStatus");
}
}
How can i fix this problems? I did search and tried from different blogs but its not working for me.

The problem is that your combobox's DataContext is User, but you need to set ItemsSource from DataGrid's datacontext. In order to do that you need to use the following syntax:
<ComboBox
ItemsSource="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type DataGrid}}, Path=DataContext.Users}"
x:Name="cbUserRole" FlowDirection="LeftToRight" FontSize="16" Foreground="MidnightBlue" HorizontalAlignment="Stretch" VerticalAlignment="Center"
SelectedItem="{Binding UserRole,Mode=TwoWay}"></ComboBox>
And Remove Loaded handler.
You can also use <DataGrid DataContext="{StaticResource uvm}" instead of codebehind code:
var userList = new UserViewModel().Users;
userDataGrid.ItemsSource = userList;
You don't have ItemsSource for your Status Combobox. I think you should add collection of statuses to UserViewModel and bind it like in the example above.

Related

Data binding a button visibility in WPF isn't working

Alot of code incoming, Some of this might be useless because i was trying alot of things to get this working.
I am trying to make the button Export Installer invisible unless the selected installer has a value of TRUE on IsDownloaded
so i'll start with my WPF
DataContext="{Binding Source={StaticResource TheViewModel}}"
Title="MainWindow" Height="458" Width="755">
<Window.Resources>
<ViewModel:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<ViewModel:InstallerColorConverter x:Key="InstallerColorConverter" />
</Window.Resources>
<ListBox Height="300" Grid.Column="0" Grid.Row="0" SelectionMode="Single" ItemsSource="{Binding SelectedProduct.Installers}" SelectedItem="{Binding SelectedInstaller}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Ellipse HorizontalAlignment="Right" Fill="{Binding InstallerRelativeToCurrent, Converter={StaticResource InstallerColorConverter}}" Margin="5,5,5,5 " Height="20" Width="20" Stroke="Black"></Ellipse>
<TextBlock Margin="5,0,5,0" Text="{Binding Name}" />
<TextBlock Text=" " />
<TextBlock Text="{Binding VersionNumber}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Column="0" Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Stretch">
<Button Name="btnInstall" Margin="5,5,5,5" Height="50" Width="75" MouseDoubleClick="btnDownloadInstaller_MouseDoubleClick" >Download</Button>
<Button Name="btnExportInstaller" Visibility="{Binding Path=SelectedInstaller.IsDownloaded, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Button}}, Converter={StaticResource BoolToVisibilityConverter}}" Margin="5,5,5,5" Height="50" Width="90" MouseDoubleClick="btnExportInstaller_MouseDoubleClick" >Export Installer</Button>
</StackPanel>
The main things to point out in that is
<Button Name="btnExportInstaller" Visibility="{Binding Path=SelectedInstaller.IsDownloaded, RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type Button}}, Converter={StaticResource BoolToVisibilityConverter}}" Margin="5,5,5,5" Height="50" Width="90" MouseDoubleClick="btnExportInstaller_MouseDoubleClick" >Export Installer</Button>
and
<ViewModel:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
now in my class BoolToVisibilityConver
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
var booool = (bool)value;
if (booool)
return Visibility.Collapsed;
else
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
// Do the conversion from visibility to bool
return Visibility.Collapsed;
}
}
Then finally in the view model i have
private Visibility _IsVisible;
public Visibility IsVisible
{
get { return _IsVisible; }
set { _IsVisible = value; OnPropertyChanged(nameof(_SelectedInstaller.IsDownloaded)); }
}
private Products _SelectedProduct;
public Products SelectedProduct
{
get { return _SelectedProduct; }
set { _SelectedProduct = value; OnPropertyChanged(nameof(SelectedProduct)); }
private Installers _SelectedInstaller;
public Installers SelectedInstaller
{
get { return _SelectedInstaller; }
set { _SelectedInstaller = value; OnPropertyChanged(nameof(SelectedInstaller)); }
It doesn't work
I'm getting these 2 errors on the console and i don't understand them
System.Windows.Data Error: 1 : Cannot create default converter to perform 'one-way' conversions between types 'System.Boolean' and 'System.Windows.Visibility'. Consider using Converter property of Binding. BindingExpression:Path=SelectedProduct.IsInstalled; DataItem='ViewModel' (HashCode=26987408); target element is 'Grid' (Name=''); target property is 'Visibility' (type 'Visibility')
System.Windows.Data Error: 5 : Value produced by BindingExpression is not valid for target property.; Value='False' BindingExpression:Path=SelectedProduct.IsInstalled; DataItem='ViewModel' (HashCode=26987408); target element is 'Grid' (Name=''); target property is 'Visibility' (type 'Visibility')
If anyone can point out what i'm doing wrong here i would love you forever!
SOLVED..this is the working code
<Window.Resources>
<ViewModel:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<Button Name="btnExportInstaller" Visibility="{Binding SelectedInstaller.IsDownloaded , Converter={StaticResource BoolToVisibilityConverter}}" Margin="5,5,5,5" Height="50" Width="90" MouseDoubleClick="btnExportInstaller_MouseDoubleClick">Export Installer</Button>
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
var booool = (bool)value;
if (booool == false)
return Visibility.Collapsed;
else
return Visibility.Visible;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value is Visibility && (Visibility)value == Visibility.Visible)
{
return true;
}
return false;
}
}
Inside viewmodel
private Installers _SelectedInstaller;
public Installers SelectedInstaller
{
get { return _SelectedInstaller; }
set { _SelectedInstaller = value; OnPropertyChanged(nameof(SelectedInstaller)); }
private Visibility _IsVisible;
public Visibility IsVisible
{
get { return _IsVisible; }
set { _IsVisible = value; OnPropertyChanged(nameof(SelectedInstaller.IsDownloaded)); }
}

WPF bind enum to combobox

In my WPF UserControl I need to bind an Enum on a ComboBox. This enum is declared locally:
public partial class ViewerDataConfiguration : UserControl
{
private ViewerDataConfigurationViewModel PageViewModel;
public Visibility IsParametriSelected { get; set; }
public IEnumerable<eDatoAlarmMode> EnumAlarmModes {
get
{
return Enum.GetValues(typeof(eDatoAlarmMode)).Cast<eDatoAlarmMode>();
}
}
On the main Grid, where there is a collection bound, I defined a ComboBox as follows:
<TextBox Grid.Column="16" Text="{Binding ConfigObject.Edit.Source}" Style="{StaticResource txtDataStyle2}" Width="30" Visibility="{Binding ConfigObject.Edit, Converter={StaticResource ListaValoriVisibilityConverter}}" HorizontalAlignment="Stretch" TextChanged="Data_TextChanged" />
<Label Grid.Column="17" Content="AlarmMode" Style="{StaticResource labelStyle2}" />
<ComboBox Grid.Column="18" Width="30"
ItemsSource="{Binding Path=EnumAlarmModes, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=local:ViewerDataConfiguration}}"
DisplayMemberPath="Value"
SelectedValuePath="Value" Style="{StaticResource comboUsersStyle}" />
Basically seems that my IEnumerable is not bound correctly. I see the elements but they're blank. Any hint?
You are using DisplayMemberPath and SelectedValuePath attributes, but your collection item type is just a simple string, and want to use this entire instance directly, so you should remove these attributes and it should work as expected.
You also need to change the field to a property as data binding works only on properties, not class fields (although x:Bind in UWP no longer has this limitation):
public IEnumerable<AlarmMode> EnumAlarmModes
{
get
{
return Enum.GetValues(typeof(AlarmMode)).Cast<AlarmMode>();
}
}
If you want to display enum values instead of names, create a value converter:
public class EnumValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return (int)value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And then use it in the ItemTemplate:
<ComboBox Grid.Column="18" Width="100"
ItemsSource="{Binding Path=EnumAlarmModes}">
<ComboBox.Resources>
<local:EnumValueConverter x:Key="EnumValueConverter" />
</ComboBox.Resources>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Converter={StaticResource EnumValueConverter}}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
For this to work you also have to add a xmlns:local declaration to your Window:
xmlns:local="clr-namespace:NamespaceOfConverter"

Getting SQL/Linq-query variable to display 3 decimals for multi-type-variable items in one ListBox

Good day
Wonder if anyone can help.
I intend to display the rows containing the items for the following
public class Product
{
[PrimaryKey, AutoIncrement]
public int ChemID { get; set; }
public string ChemCat { get; set; }
public string ChemName { get; set; }
public double ChemWeight { get; set; }
}
in the form of Xaml ListBox
<ListBox Name="listChems" Height="200" Width="300" HorizontalAlignment="Left" Margin="0,5,0,0" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Height="18" VerticalAlignment="Top" >
<TextBlock Text="{Binding ChemCat}" Margin="10,0,0,0" Width="90" FontSize="14"/>
<TextBlock Text="{Binding ChemName}" Margin="10,0,0,0" Width="90" FontSize="14"/>
<TextBlock Text="{Binding ChemWeight}" Margin="10,0,0,0" Width="60" FontSize="14"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
using the usual SQL/Linq query
private void btnDisplayAll_Click(object sender, RoutedEventArgs e)
{
var display = conn.Table<Product>()
.Select(g => g);
listChems.ItemsSource = display;
}
ChemCat and ChemName are purely strings eg. Lanthanide and Neodymium respectively.
However, I need to have ChemWeight (type double) to be displayed in 3 decimals, even if the user provide a number that has 0 or 1 or 2 decimals, ie.
1 to be displayed as 1.000
1.4 to be displayed as 1.400
1.42 to be displayed as 1.420
How do I get SQL/Linq-query variable 'display' to force {Binding ChemWeight} to display 3 decimals, considering that the other members ChemCat and ChemName are strings and they are all tied together in a one ListBox.
(Earlier post said ChemPrice, it was meant to be ChemWeight)
Many thanks.
For WPF
Use the StringFormat property in the binding.
<TextBlock Text="{Binding ChemPrice, StringFormat={}{0:0.000}}" Margin="10,0,0,0" Width="60" FontSize="14"/>
For WINRT
Use a IValueConverter
public class StringFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return string.Format((string)parameter, value);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
Note that you need to specify your converter for your control or window.
<UserControl.Resources>
<local:StringFormatConverter x:Key="Converter"></local:StringFormatConverter>
</UserControl.Resources>
<Grid>
<TextBlock Text="{Binding ChemPrice, Converter={StaticResource Converter}, ConverterParameter=\{0:0.00\}}" Margin="10,0,0,0" Width="60" FontSize="14"/>
</Grid>

WPF binding from parent to child element

Here is my XAML
<ListView x:Name="missingVariablesListView" ScrollViewer.CanContentScroll="True" HorizontalAlignment="Left" Height="320" Margin="81,28,0,0" VerticalAlignment="Top" Width="641" ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<TextBlock Text="ComponentID: " FontWeight="Bold" Foreground="Brown" />
<TextBlock Text="{Binding Name}"/>
</StackPanel>
<ItemsControl ItemsSource="{Binding Parameters}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Variable Name: " Foreground="Green"/>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text=" "/>
<TextBlock Text="Variable Value: " Foreground="Blue"/>
<TextBlock Text="{Binding Value}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style >
<Style.Triggers>
<DataTrigger Binding="{Binding IsMissing}" Value="false">
<Setter Property="UIElement.Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
<TextBlock Text="-----------------------------------------------------------------"/>
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Here is the CodeBehind
this.missingVariablesListView.DataContext = //Code to fill in the list View
Classes Involved
public class Component
{
private ObservableCollection<ComponentParameter> parameters = new ObservableCollection<ComponentParameter>();
public string Name
{
get;
set;
}
public ObservableCollection<ComponentParameter> Parameters
{
get{return parameters;}
set{parameters = value;}
}
}
public class ComponentParameter
{
public string Name
{
get;set;
}
public string Value
{
get;set;
}
public bool HasErrors
{
get;
set;
}
public bool IsMissing
{
get;set;
}
Sample Output (at the moment)
ComponentID: Component1
--------------------------
ComponentID: Component2
VariableName:Var1 Variable Value:Val1
VariableName:Var2 Variable Value:Val2
-----------------------
ComponentID: Component3
-----------------------
ComponentID: Component4
-----------------------
What i want to do is that whenever the boolean IsMissing is true for the inner element within the itemsControl i want to make sure that the ComponentID and the Name properties within the StackPanel (that has the orientation as horizontal) are not shown in the window including the child elements. Basically i am trying to find a way to exclude the whole description for that particular ComponentID whose isMissing variable is set to true. Any suggestions on this?
I would consider to add a property IsAnyParameterMissing to the Component class:
public class Component
{
private ObservableCollection<ComponentParameter> parameters = new ObservableCollection<ComponentParameter>();
public string Name
{
get;
set;
}
public ObservableCollection<ComponentParameter> Parameters
{
get{return parameters;}
set{parameters = value;}
}
public bool IsAnyParameterMissing
{
get { return this.Parameters.Any(param => param.IsMissing); }
}
}
And then bind the visibility to this property:
<StackPanel Orientation="Horizontal" Visibility="{Binding IsAnyParameterMissing, Converter={BooleanToVisibilityConverter}}">
<TextBlock Text="ComponentID: " FontWeight="Bold" Foreground="Brown" />
<TextBlock Text="{Binding Name}"/>
</StackPanel>
This will only display the StackPanel if any of the Parameters in the collection has its IsMissing property equal to true. Note that this will not change the visibility if the IsMissing property is changed for any of the items! This would need some additional work.
EDIT: Note that the {BooleanToVisibilityConverter} might need to be adjusted, depending on your available converters. Should be a simple task though.
One thing i noticed is that you don't implement the properties (such as "isMissing") as DependencyProperty. Neither do you use property change notifications via INotifyPropertyChanged.
You have to implement either of the two, otherwise changes of the properties will not be propagated through the bindings. That means, the trigger wouldn't trigger...
You could use a BooleanToVisibilityConverter to hide those items for you, although you may need to change your property or add a new IsPresent property for this:
In Resources:
<Converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter"
IsInverted="True" />
In your ListView.ItemTemplate:
<StackPanel Orientation="Horizontal" Visibility="{Binding IsMissing, Converter={
StaticResource BoolToVisibilityConverter}}">
<TextBlock Text="ComponentID: " FontWeight="Bold" Foreground="Brown" />
<TextBlock Text="{Binding Name}"/>
</StackPanel>
Custom BoolToVisibilityConverter with IsInverted property:
[ValueConversion(typeof(bool), typeof(Visibility))]
public class BoolToVisibilityConverter : IValueConverter
{
public bool IsInverted { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || value.GetType() != typeof(bool)) return null;
bool boolValue = IsInverted ? !(bool)value : (bool)value;
return boolValue ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || value.GetType() != typeof(Visibility)) return null;
if (IsInverted) return (Visibility)value != Visibility.Visible;
return (Visibility)value == Visibility.Visible;
}
}
Now it will just take you one minute to implement it. I have updated the XAML example above to use it.

WPF converter to update in real time background colour of textbox on text change

I have two textboxes for the firstname and second name of a user and I have created a converter to change the background colour of the textbox when the text equals a specific string. The problem I am having is that the textbox will only update at run time and doesn't update when I change the text is the textbox.
XAML:
<TextBox x:Name="forenameTextBox" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="3" Grid.Row="1"
Background="{Binding Staff,Converter ={StaticResource StaffNameToBackgroundColourConverter1}}"
Text="{Binding Staff.Forename, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" VerticalAlignment="Center" Width="120"/>
<Label Content="Surname:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="2" VerticalAlignment="Center"/>
<TextBox x:Name="surnameTextBox" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="3" Grid.Row="2"
Background="{Binding Staff,Converter={StaticResource StaffNameToBackgroundColourConverter1}}"
Text="{Binding Staff.Surname, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" VerticalAlignment="Center" Width="120"/>
Converter code:
public class StaffNameToBackgroundColourConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var staff = (Staff) value;
if (staff.Forename == "Donald" && staff.Surname == "Duck")
{
return "Yellow";
}
else
{
return "White";
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
Correct text input:
Wrong text input - no change:
You need to add UpdateSourceTrigger=PropertyChanged to your Binding:
<TextBox x:Name="forenameTextBox" Grid.Column="1" HorizontalAlignment="Left"
Height="23" Margin="3" Grid.Row="1" Background="{Binding Staff,
UpdateSourceTrigger=PropertyChanged, Converter ={StaticResource
StaffNameToBackgroundColourConverter1}}" Text="{Binding Staff.Forename,
Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"
VerticalAlignment="Center" Width="120"/>
<TextBox x:Name="surnameTextBox" Grid.Column="1" HorizontalAlignment="Left" Height="23"
Margin="3" Grid.Row="2" Background="{Binding Staff,
UpdateSourceTrigger=PropertyChanged, Converter={StaticResource
StaffNameToBackgroundColourConverter1}}" Text="{Binding Staff.Surname, Mode=TwoWay,
NotifyOnValidationError=true, ValidatesOnExceptions=true}"
VerticalAlignment="Center" Width="120"/>
This will update the binding source as the user types each letter. You can find out more from the Binding.UpdateSourceTrigger Property page at MSDN.
You should return some brush object than colors to background like below
public class StaffNameToBackgroundColourConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo
culture)
{
var staff = (Staff)value;
if (staff.Forename == "Donald" && staff.Surname == "Duck")
{
return new SolidColorBrush(Colors.Yellow);
}
else
{
return new SolidColorBrush(Colors.White);
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
First, you added the UpdateSourceTrigger=PropertyChanged to the wrong binding. You have to add it to the binding of the Text property.
Second, you bound the Text property to Staff.Forename but the Background to Staff. The Background property doesn't know that Staff has changed when you write in Staff.Forename. You have to raise the PropertyChanged event for the Staff property when you write in the Staff.Forename property. Same for Staff.Surname.

Categories