I'm using ComboBox in my xaml but i'm unable to visualise any data on the view. It just shows empty text file and empty dropdown.
I have tried to debug the problem with help of these tips. However, I haven't been able to solve the issue.
Here's the databindingDebugConverter:
public class DatabindingDebugConverter : IValueConverter
{
public object Convert(object value1, Type targetType, object parameter, CultureInfo culture)
{
Debugger.Break();
return value1;
}
public object ConvertBack(object value2, Type targetType, object parameter, CultureInfo culture)
{
Debugger.Break();
return value2;
}
}
The Value1 is returning in ComboBox Text= case a "Field Device" (object{string})
and on the ItemsSource= value1 is returning object{Device} with the fields of Category and reference to Category1 object holding CategoryId in it.
For the SelectedValue a "Field Device" (object{string}) is once again returned.
Here is the ComboBox xaml:
<ComboBox x:Name="ProductCategoryComboBox" HorizontalAlignment="Right" Height="21.96" Margin="0,20,10.5,0" VerticalAlignment="Top" Width="100"
Text="{Binding DeviceDatabaseViewModel.SelectedDevice.Category, UpdateSourceTrigger=PropertyChanged, Converter={StaticResource debugConverter}}"
IsEditable="False"
ItemsSource="{Binding DeviceDatabaseViewModel.SelectedDevice, Converter={StaticResource debugConverter}}"
SelectedValue="{Binding DeviceDatabaseViewModel.SelectedDevice.Category, Mode=TwoWay, Converter={StaticResource debugConverter}}"
SelectedValuePath="CategoryId"
DisplayMemberPath="Category" />
Similar binding including TextBlock fields within the xaml are working fine and displaying the string values from the SelectedDevice.
EDIT
The selectedDevice is referred from a dataGrid:
private Device _selectedDevice;
public Device SelectedDevice
{
get
{
return _selectedDevice;
}
set
{
if (_selectedDevice == value)
{
return;
}
_selectedDevice = value;
RaisePropertyChanged("SelectedDevice");
}
}
A Combobox is for choosing an Item out of a Collection (for example List or ObservableCollection if you want the UI to recognize changes in the collection).
You do not bind to a collection here:
ItemsSource="{Binding DeviceDatabaseViewModel.SelectedDevice, Converter={StaticResource debugConverter}}"
Instead of binding to the SelectedDevice you would need to bind to an ObservableCollection AllDevices or something like this to which you then could bind the ItemsSource.
Here an example for something you could bind to:
public class DeviceDatabaseViewModel
{
public ObservableCollection<Device> AllDevices
{
get; set;
}
public DeviceDatabaseViewModel()
{
AllDevices = new ObservableCollection<Device>();
AllDevices.Add(new Device { Category = 'Computer', CategoryId = 1 }, new Device { Category = 'Tablet', CategoryId = 2 });
}
}
Then using the following binding:
ItemsSource="{Binding DeviceDatabaseViewModel.AllDevices, Converter= {StaticResource debugConverter}}"
Related
Everytime i change my selected item inside my UI it's not updating my combobox.
XAML
<ComboBox x:Name="CompanyComboBox" HorizontalAlignment="Left" Height="26"
Margin="100,41,0,0" VerticalAlignment="Top" Width="144"
SelectionChanged="CompanyComboBox_SelectionChanged"
SelectedItem="{Binding SelectedCompany, UpdateSourceTrigger=PropertyChanged, Mode=OneWayToSource}">
<ComboBox.ItemTemplate>
<DataTemplate>
<ContentPresenter
Content="{Binding Converter={StaticResource DescriptionConverter}}">
</ContentPresenter>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
C#
private Company _selectedCompany;
public Company SelectedCompany
{
get { return _selectedCompany; }
set
{
if (value == _selectedCompany)
return;
_selectedCompany = value;
OnPropertyChanged(nameof(SelectedCompany));
}
}
Just to clarify the Company class is actually an ENUM
DescriptionConverter:
public class CompanyDescriptionConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var type = typeof(Company);
var name = Enum.GetName(type, value);
FieldInfo fi = type.GetField(name);
var descriptionAttrib = (DescriptionAttribute)
Attribute.GetCustomAttribute(fi, typeof(DescriptionAttribute));
return descriptionAttrib.Description;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
what i mean by inside my UI, is i have a list of companies set to a combobox item source, and when i change the combobox value to another company, it doesn't update in my source, it stay's as default.
My Enum might clarify the problem for someone:
[Description("Netpoint Solutions")]
Company1 = 0,
[Description("Blackhall Engineering")]
Company2 = 180,
Try to remove Mode=OneWayToSource and your event handler:
SelectionChanged="CompanyComboBox_SelectionChanged"
You don't need to handle the SelectionChanged event when you bind the SelectedItem property.
Also make sure that you set the DataContext to an instance of your class where the SelectedCompany property is defined.
Your Binding is defined as OneWayToSource. When you want it to be updated from the viewModel, you should set it to TwoWay.
See the documentation of Bindings for more details: https://msdn.microsoft.com/de-de/library/ms752347(v=vs.110).aspx
EDIT:
I skipped the part where you have an Enum as the source. I do not see, that you define the ItemsSource for the Combobox, where is this done? The value passed to the setter of SelectedCompany has to be in the Collection defined as ItemsSource.
For Enums, you can refer to this thread: How to bind an enum to a combobox control in WPF?
I have a ComboBox like this
<ComboBox
Grid.Column="1"
Padding="5,0,0,0"
DisplayMemberPath="Description"
SelectedItem="{Binding MaxXXAge, Mode=TwoWay, Converter={StaticResource MaxXXAgeToMaxXXAgeMemberConverter}}"
ItemsSource="{Binding ElementName=SettingsXXScrollViewer, Path=DataContext.MaxXXAgeMemberGroup, Mode=OneWay}" />
However, after initialization, the combobox is blank. It actually works fine after this. I can select and show the selected item as expected. It's just the first glance doesn't work. However, I already initialized MaxXXAge and the converter has been triggered. Here is the group
public IReadOnlyList<MaxXXAgeMembers> MaxXXAgeMemberGroup { get { return MaxXXAgeMembers.Options; } }
And this is the definition for MaxXXAgeMembers
public class MaxXXAgeMembers
{
public MaxXXAge MaxXXAge { get; private set; }
public string Description { get; private set; }
public static readonly IReadOnlyList<MaxXXAgeMembers> Options = new ReadOnlyCollection<MaxXXAgeMembers>(new[]
{
new MaxXXAgeMembers { MaxXXAge = MaxXXAge.OneDay, Description = Strings.SettingSync_OneDay},
.......
});
public static MaxXXAgeMembers FromMaxXXAge(MaxXXAge maxXXAge)
{
return Options.First(option => option.MaxXXAge == maxXXAge);
}
}
//Added the Overriding Equals later
public override bool Equals(object obj)
{
if (obj == null || !(obj is MaxEmailAgeMembers))
return false;
return ((MaxEmailAgeMembers)obj).Description.Equals(this.Description);
}
public override int GetHashCode()
{
return this.Description.GetHashCode();
}
The converter is like this
public sealed class MaxEmailAgeToMaxEmailAgeMemberConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return WPSettingsEmailViewModel.MaxEmailAgeMembers.FromMaxEmailAge((MaxEmailAge)value);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
return ((WPSettingsEmailViewModel.MaxEmailAgeMembers)value).MaxEmailAge;
}
}
Any idea?
It's blank because you don't have anything selected in the first place. If I'm not mistaken, you have to either use SelectedItem to bind your selection or SelectedValue with SelectedValuePath.
I actually never use SelectedValue with SelectedValuePath myself, so after initializing collection of items which ComboBox.ItemSource will be binded to - for example ObservableCollection<Person> Persons {get; set;} - I also set selected item property Person SelectedPerson {get; set;} to one of the values from collection. Then I bind ComboBox.SelectedItem to this property, so on initialization it shows predefined selected value.
I guess you can achieve the same with SelectedValue and SelectedValuePath, but you have to use them together as described here.
I used x:Bind for the ItemResource and added ViewModel inside code behind, and solved this problem.
You ComboBox isn't blank, but it don't know how to render your MaxXXAgeMembers. You should use ItemTemplate to tell this to him. For Ex:
<ComboBox
Grid.Column="1"
Padding="5,0,0,0"
SelectedValue="{Binding MaxXXAge, Mode=TwoWay, Converter={StaticResource MaxXXAgeToMaxXXAgeMemberConverter}}"
ItemsSource="{Binding ElementName=SettingsXXScrollViewer, Path=DataContext.MaxXXAgeMemberGroup, Mode=OneWay}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Description}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
I'm using the following control template in two windows that are opened at the same time and both using the SAME viewmodel.
Here is the template;
<ControlTemplate x:Key="SecurityTypeSelectionTemplate">
<StackPanel>
<RadioButton GroupName ="SecurityType" Content="Equity"
IsChecked="{Binding Path=SecurityType, Mode=TwoWay, Converter={StaticResource EnumBoolConverter}, ConverterParameter=Equity}" />
<RadioButton GroupName ="SecurityType" Content="Fixed Income"
IsChecked="{Binding Path=SecurityType, Mode=TwoWay, Converter={StaticResource EnumBoolConverter}, ConverterParameter=FixedIncome}" />
<RadioButton GroupName ="SecurityType" Content="Futures"
IsChecked="{Binding Path=SecurityType, Mode=TwoWay, Converter={StaticResource EnumBoolConverter}, ConverterParameter=Futures}" />
</StackPanel>
</ControlTemplate>
Here is the viewmodel property:
private SecurityTypeEnum _securityType;
public SecurityTypeEnum SecurityType
{
get { return _securityType; }
set
{
_securityType = value; RaisePropertyChanged("SecurityType");
}
}
Here's the Enum:
public enum SecurityType { Equity, FixedIncome, Futures }
Here is the converter:
public class EnumToBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object enumTarget, CultureInfo culture)
{
string enumTargetStr = enumTarget as string;
if (string.IsNullOrEmpty(enumTargetStr))
return DependencyProperty.UnsetValue;
if (Enum.IsDefined(value.GetType(), value) == false)
return DependencyProperty.UnsetValue;
object expectedEnum = Enum.Parse(value.GetType(), enumTargetStr);
return expectedEnum.Equals(value);
}
public object ConvertBack(object value, Type targetType, object enumTarget, CultureInfo culture)
{
string expectedEnumStr = enumTarget as string;
if (expectedEnumStr == null)
return DependencyProperty.UnsetValue;
return Enum.Parse(targetType, expectedEnumStr);
}
}
The problem is a bit strange. I have two windows that are showing slightly different views of the SAME ViewModel. The same template shown above is reused in both views.
If Equity is initially set as SecurityType, i can change this to FixedIncome by clicking on the relevant radio button. I can not then change it back to Equity.
I can however set it to Futures. But then after that, i can not change it to either FixedIncome or Equity by clicking the relevant radio buttons.
What's happening in the cases where i can not set change it back is that the Setter is called twice. the first time it's setting the value to the correct selected value, but the moment RaisePropertyChanged is fired,
the setter is invoked again, this time with the original value.
It feels like when RaisePropertyChanged, the the setter is being called by the binding from the 2nd window, thus overwriting the value being set in the first window where the user makes the selection.
Does anyone know if this is the case and how to avoid in this scenario?
Here's my version of EnumToBoolConverter:
public class EnumToBoolConverter : BaseConverterMarkupExtension<object, bool>
{
public override bool Convert(object value, Type targetType, object parameter)
{
if (value == null)
return false;
return value.Equals(Enum.Parse(value.GetType(), (string)parameter, true));
}
public override object ConvertBack(bool value, Type targetType, object parameter)
{
return value.Equals(false) ? DependencyProperty.UnsetValue : parameter;
}
}
The default behavior for a RadioButton is to update the source when the property changes so both windows are trying to update the source. One fix is to only update the source only from where the user clicked. To do this use Binding.UpdateSourceTrigger Explict on the binding. Add a click handler in code behind for RadioButton. In it explicity update the source.
<StackPanel>
<RadioButton GroupName ="SecurityType" Content="Equity"
IsChecked="{Binding Path=SecurityType, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, UpdateSourceTrigger=Explicit, ConverterParameter=Equity}" Click="RadioButton_Click" />
<RadioButton GroupName ="SecurityType" Content="Fixed Income"
IsChecked="{Binding Path=SecurityType, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, UpdateSourceTrigger=Explicit, ConverterParameter=FixedIncome}" Click="RadioButton_Click"/>
<RadioButton GroupName ="SecurityType" Content="Futures"
IsChecked="{Binding Path=SecurityType, Mode=TwoWay, Converter={StaticResource EnumToBoolConverter}, UpdateSourceTrigger=Explicit, ConverterParameter=Futures}" Click="RadioButton_Click"/>
</StackPanel>
private void RadioButton_Click(object sender, RoutedEventArgs e)
{
BindingExpression be = ((RadioButton)sender).GetBindingExpression(RadioButton.IsCheckedProperty);
be.UpdateSource();
}
You may have to use a UserControl instead of or inside your ControlTemplate to get code behind in your view.
I am fairly new to WPF so forgive me if I am missing something obvious. I'm having a problem where I have a collection of AggregatedLabels and I am trying to bind the ItemCount of each AggregatedLabel to the FontSize in my DataTemplate so that if the ItemCount of an AggregatedLabel is large then a larger fontSize will be displayed in my listBox etc. The part that I am struggling with is the binding to the ValueConverter. Can anyone assist? Many thanks!
XAML Snippet
<DataTemplate x:Key="TagsTemplate">
<WrapPanel>
<TextBlock Text="{Binding Name, Mode=Default}"
TextWrapping="Wrap"
FontSize="{Binding ItemCount,
Converter={StaticResource CountToFontSizeConverter},
Mode=Default}"
Foreground="#FF0D0AF7"/>
</WrapPanel>
</DataTemplate>
<ListBox x:Name="tagsList"
ItemsSource="{Binding AggregatedLabels, Mode=Default}"
ItemTemplate="{StaticResource TagsTemplate}"
Style="{StaticResource tagsStyle}"
Margin="200,10,16.171,11.88" />
With your CollectionView in place you might be able to bind to the Groups property, i've never used that, will try it and clarify if possible...
Edit: Allright, here's one way to do it:
The data you bind to needs to be the CollectionView.Groups, the CollectionView should be defined like this:
CollectionView view = (ListCollectionView) CollectionViewSource.
GetDefaultView(LabelData);
view.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
Then you can bind to the respective properties of CollectionViewGroup in code, what you need are probably:
ItemCount
Name
That being said your original binding should work.
Note: You only pass one value to the converter, the ItemCount, thus it should look like this:
public class CountToFontSizeConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter,
CultureInfo culture)
{
const int minFontSize = 6;
const int maxFontSize = 38;
const int increment = 3;
if ((minFontSize + (int)value + increment) < maxFontSize)
{
return (double)(minFontSize + (int)value + increment);
}
return (double)maxFontSize;
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Edit: Further clarifications...
Just add the CollectionView to your ViewModel as a property and create it in its constructor:
public class TagCloudViewModel//:INotifyPropertyChanged
{
public ObservableCollection<AggregatedLabelModel> AggregatedLabels
{get; set;}
public CollectionView AggregatedLabelsView {get; set;} // <-This...
public TagCloudViewModel()
{
var data = new DataAccess();
AggregatedLabels = data.GetData();
//...and this:
AggregatedLabelsView = (ListCollectionView)CollectionViewSource.
GetDefaultView(AggregatedLabels);
AggregatedLabelsView.GroupDescriptions.Add(
new PropertyGroupDescription("Name"));
}
}
Then bind to AggregatedLabelsView.Groups.
I'm a newbie in WPF, so it probably is something very basic that I'm forgetting to do but I can't see what it is.
I have a window with a combobox that display some data, I want the user to select a category in this combobox. It's working partially. The window show the combobox, starting with no selection, then the user choose a item, and it's set, but if the user try to change to other item, nothing works, it keeps the original selected item.
Here's me code:
[Category class]
public class Category {
public long CategoryId { get; set; }
public string Name { get; set; }
public Category MotherCategory { get; set; }
public ICollection<Category> Categories { get; set; }
public int Align { get; set; }
}
[ComboBox XAML]
<ComboBox Grid.Column="1" x:Name="motherCategoryComboBox" Margin="0,6,12,1"
IsSynchronizedWithCurrentItem="True">
<ComboBox.Resources>
<converter:LeftMarginConverter x:Key="LeftMarginConverter" />
</ComboBox.Resources>
<ComboBox.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Categories}">
<TextBlock Text="{Binding Path=Name}" Margin="{Binding Path=Align, Converter={StaticResource LeftMarginConverter}}" />
</HierarchicalDataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
[Window code-behind file]
public CategoryWindow()
{
InitializeComponent();
db = new JaspeContext();
categorieslist = db.Categories.ToList();
motherCategoryComboBox.ItemsSource = categorieslist;
Title = "Add category";
}
[The converter]
public class LeftMarginConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double leftMargin = double.Parse(value.ToString());
if (leftMargin != 1)
leftMargin = leftMargin * 9;
return new Thickness(leftMargin, 0, 0, 0);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new Exception("The method or operation is not implemented.");
}
}
Need your help. This is making me crazy!
Thanks!!
I hope I understood your question correctly. Is your DataContext a Category object? Sounds to me like you need to bind the SelectedItem property of the ComboBox.
E.g.:
<ComboBox Grid.Column="1" x:Name="motherCategoryComboBox" Margin="0,6,12,1"
IsSynchronizedWithCurrentItem="True" SelectedItem="{Binding MotherCategory , Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
It's not your case but since it happened to me I'm publishing this here, to help other people who might stumble upon this issue...
During the comboBox SelectionChangeCommitted() event handler I added the following line:
combobox.Text = combobox.Text.Trim();
what it did is reset the selectedIndex and selectedText properties and didn't allow them to change to the new value due to keyboard or mouse input.