I have a combobox and I am binding the combobox in XAML with viewmodel dictionary values.
When the page is loaded for first time I am trying to download the dictionary values and from server and set it to the dictionary view model variable.
But the combobox looks empty I don't understand why this happens because View Model variable has been updated and thats supposed to trigger the reload of combobox and thats not happening..
FYI:
If I hardcode the dictionary rather than downloading it from server I don't see this problem
When I load the page second time I don't see this problem
Update
XAML
<ComboBox x:Name=“testBox” Margin=“0,0,0,0” PlaceholderText="{StaticResource testText}” ItemsSource="{Binding TestDictionary.Values}” SelectedValue="{Binding DictionaryValue, Mode=TwoWay}" IsEnabled="{Binding IsItLoading, Converter={StaticResource InverseBooleanConverter}}"/>
View Model
private Dictionary<string, string> testDictionary;
public Dictionary<string, string> TestDictionary
{
get
{
if (this.testDictionary == null)
{
this.testDictionary = new Dictionary<string, string>();
}
return this.testDictionary;
}
set
{
this.Set(() => this.TestDictionary, ref this.testDictionary, value);
}
}
The Dictionary does not provides notifications when items get added, removed, or when the entire list is refreshed.
When we add new data to the Dictionary, we should be able to set null to the ItemsSource of the ComboBox. Then set the TestDictionary.Values to ItemsSource of the ComboBox.
You can also implemented your own ObservableDictionary. When the Dictionary changes, the ComboBox will be changed.
To implement the ObservableDictionary, you can refer the following question.
Related
I have a converter which takes in the ListView as a parameter to for each item as the List, and then, using the ItemsSource and IndexOf, I can determine the items position in the list and return that as a number for the View:
public class MyConverter : IValueConverter, IMarkupExtension
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var listView = parameter as ListView;
var collection = listView.ItemsSource as IList;
object item = value;
var answer = collection.Count - collection.IndexOf(item); //I'm actually numbering items in reverse.
return answer;
}
}
I bind to it using the entire binding context ( {Binding .} ) so that each item is passed as a value to it's own converter, like so:
<ListView x:Name="ListViewItself" ItemsSource="{Binding TheItemSource}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding ., Converter={converters:MyConverter}, ConverterParameter={x:Reference ListViewItself}}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This works very well. As I add items to my list, the converter correctly calculates the right item place.
My issue now, is that when I remove items from the collection and the ListView, the values for each remaining item in the ListView should automatically update, so as to take into account their new positions. But this is not the case. The converters are called, but with many null values for the items.
I want to know, how can I call RaisePropertyChanged for each binding context in the list so that the converter fires properly for all of them?
Edit:
I try to use
RaisePropertyChanged(nameof(TheItemSource)) when I remove an item from the collection, but it doesn't fire the converter for each item like I'd expect.
I'm extending a control to be able to reuse it across my current Xamarin project. As part of this control, I need to create a DataTemplate programmatically. I have this part figured out and it works ok.
The DataTemplate has a Label in it. I need to bind the Label's BindingContext property to {Binding Source}. I need to bind the Label's Text property to {Binding Path=Name}.
This works in XAML, but I don't want to have to copy it to a million different places in the code base.
<dxGrid:TemplateColumn FieldName="MyPropertyName"
Caption="MyColumn">
<dxGrid:TemplateColumn.DisplayTemplate>
<DataTemplate>
<Label BindingContext="{Binding Source}"
Text="{Binding Source, Path=MyPropertyName}" />
</DataTemplate>
</dxGrid:TemplateColumn.DisplayTemplate>
My extended control looks like this right now:
public class MyColumn : TemplateColumn
{
public MyColumn()
{
DataTemplate displayTemplate = new DataTemplate(() =>
{
BindingBase textBinding = new Binding(FieldName);
Label label = new Label();
// TODO: Bind BindingContextProperty to {Binding Source}
//label.SetBinding(BindingContextProperty, binding);
label.SetBinding(Label.TextProperty, textBinding);
return new ViewCell
{
View = label
};
});
DisplayTemplate = displayTemplate;
}
}
I'm getting hung up in the binding because I'm not sure how to do the equivalent of {Binding Source} in code. Any help would be appreciated.
#Eugene - Thanks for the response. Unfortunately this does not work and binding to "Source" like that throws a Null Reference Exception. I made another pass at it this morning and got it working this way:
public MyColumn()
{
DataTemplate displayTemplate = new DataTemplate(() =>
{
Grid grid = new Grid();
grid.SetBinding(Grid.BindingContextProperty, "Source");
Label label = new Label();
label.SetBinding(Label.TextProperty,FieldName);
grid.Children.Add(label);
return grid;
});
this.DisplayTemplate = displayTemplate;
}
It's simple, use name of property
label.SetBinding(BindingContextProperty, "Source");
I've implemented a UWP SplitView similar to the one made by Diederik Krols. I prefer the approach of using a ListView over using RadioButtons as shown by Jerry Nixon's implementation of the SplitView.
However, I have a problem when I add secondary commands at the bottom of the SplitView, which Diederik doesn't do. These secondary commands are implemented by another ListView bound to a collection of Commands. So I have TWO ListViews that should only allow ONE item to be selected among them at a time.
I've tried two things:
I've bound the SelectedItem property of both ListViews to the same object. The idea was that maybe ListView doesn't display a selection if SelectedItem is not in the list bound to ItemsSource. Sadly, it simply goes on displaying the last selected item.
I've bound each ListView's SelectedItem to its own property. When one of the ListViews' item is selected, the SelectedItem of the other property is set to 'null' by the ViewModel. This produces the same result as in 1.
Any ideas on how to solve this problem?
I had the same problem.
I have a fix, but I'm not that proud of it ;) it's a dirty hack and I'm hoping other solutions will present itself so I can change it too.
But here it is:
First the listviews hook up to the SelectionChanged event even though we also bind the selected item to the viewmodel ( full code shown here https://github.com/AppCreativity/Kliva/blob/master/src/Kliva/Controls/SidePaneControl.xaml )
<ListView x:Name="TopMenu"
SelectionChanged="OnTopMenuSelectionChanged"
Background="Transparent"
ItemsSource="{x:Bind ViewModel.TopMenuItems}"
ItemTemplateSelector="{StaticResource MenuItemTemplateSelector}"
ItemContainerStyle="{StaticResource MenuItemContainerStyle}"
SelectedItem="{x:Bind ViewModel.SelectedTopMenuItem, Mode=TwoWay, Converter={StaticResource XBindItemCastingConverter}}"
Grid.Row="0" />
In that SelectionChanged, we'll deselect the 'other' listviews selection! ( full code shown here https://github.com/AppCreativity/Kliva/blob/master/src/Kliva/Controls/SidePaneControl.xaml.cs )
Note that we need to keep track that we are already in a deselecting process otherwise we'll end up with an endless loop. This is done with the _listViewChanging field.
private void OnBottomMenuSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (!_listViewChanging)
{
_listViewChanging = true;
TopMenu.SelectedIndex = -1;
_listViewChanging = false;
}
}
Last thing to do is making sure we handle the selection and clear it again in the viewmodel for next itteration ( full code shown here https://github.com/AppCreativity/Kliva/blob/master/src/Kliva/ViewModels/SidePaneViewModel.cs )
public MenuItem SelectedBottomMenuItem
{
get { return _selectedBottomMenuItem; }
set
{
if (Set(() => SelectedBottomMenuItem, ref _selectedBottomMenuItem, value))
{
if (value != null)
{
if (string.IsNullOrEmpty(SelectedBottomMenuItem.Title))
HamburgerCommand.Execute(null);
if (SelectedBottomMenuItem.Title.Equals("settings", StringComparison.OrdinalIgnoreCase))
SettingsCommand.Execute(null);
SelectedBottomMenuItem = null;
}
}
}
}
I'm using a content dialog do display instance data when an item in a grid is selected.
In the calling page's view model, when an item is selected the following method is executed.
public virtual void ItemSelected(object sender, object parameter)
{
var arg = parameter as Windows.UI.Xaml.Controls.ItemClickEventArgs;
var clickedItem = arg.ClickedItem;
var item = clickedItem as ItemsModel;
var dialog = new ItemsDialog();
dialog.DataContext = item;
dialog.ShowAsync();
}
This shows the dialog, and the content is displayed as expected. Now I'm trying to split my xaml into different templates and I'm trying to use a ContentControl to display the appropriate template. I've written a DataTemplateSelector to help choose the correct template, but now I cannot figure out the data binding for the ContentControl (see simplified version below).
<ContentDialog.Resources>
<UI:MyTemplateSelector x:Key="MyTemplateSelector"
Template1="{StaticResource Template1}"
Template2="{StaticResource Template2}"/>
<DataTemplate x:Key="Template1"/>
<DataTemplate x:Key="Template2"/>
</ContentDialog.Resources>
<StackPanel>
<ContentControl DataContext="{Binding}"
ContentTemplateSelector="{StaticResource MyTemplateSelector}"/>
</StackPanel>
When debugging into my ContentTemplateSelector, my binding is always null. I've tried various forms of the binding syntax with no luck. How do I properly set the DataContext of the ContentControl to that of the ContentDialog?
When debugging into my ContentTemplateSelector, my binding is always
null
You need to set data binding for the Content property of ContentControl control, see Remarks in MSDN:
The Content property of a ContentControl can be any type of object,
such as a string, a UIElement, or a DateTime. By default, when the
Content property is set to a UIElement, the UIElement is displayed in
the ContentControl. When Content is set to another type of object, a
string representation of the object is displayed in the
ContentControl.
So the following xaml should work:
<StackPanel>
<ContentControl Content="{Binding}"
ContentTemplateSelector="{StaticResource MyTemplateSelector}"/>
</StackPanel>
Check my completed sample in Github
You have to bind Content also.
Content="{Binding}"
You have the data source (DataContext) and how the data is displayed (templates) and now you need to specify which of the properties brings that together.
I ran into a weird problem working with the Windows Phone 8 pivot control.
Apparently, when the Pivot is bound to a list of models and the HeaderTemplate is used for binding to one of the model's properties, changing the property during runtime causes layout problem in the headers.
Here is the sample code.
Create a simple model class.
public class MyModel: INotifyPropertyChanged
{
private string _displayName;
public event PropertyChangedEventHandler PropertyChanged;
public string DisplayName
{
get
{
return _displayName;
}
set
{
_displayName = value;
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("DisplayName"));
}
}
}
}
Then initialize the model list (could occur in the ViewModel, or page's code)
Items = new List<MyModel>
{
new MyModel { DisplayName = "model 1" },
new MyModel { DisplayName = "model 2" },
}
Connecting the list of models to the pivot control
<phone:Pivot ItemsSource="{Binding Items}" DisplayMemberPath="DisplayName">
<phone:Pivot.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding DisplayName}" />
</DataTemplate>
</phone:Pivot.HeaderTemplate>
</phone:Pivot>
So now we have 2 pivot items and the headers are displayed correctly. Now we change DisplayName property of the first item during app runtime (say following a button click) and assign a longer string value:
Items[0].DisplayName = "Some other header";
This causes the headers to overlap.
Any thoughts?
Elad
if you set displayName property to "Some other header" at first time, and then change the value to "model 2", normal display. So, in my opinion, this problem was caused by the pivot header width property(in your code,the textblock's width). When the pivot was loaded, every header had fixed width, and at this time, you changed the header display name to a longger value, the pivot will not be display correctly.