WinRT ComboBox SelectedValue is null - xaml

I have created a class for creating the item to add to combobox
public class ComboBoxItemClass
{
public string Text { get; set; }
public object Value { get; set; }
public override string ToString()
{
return Text;
}
}
My XAML is as follows for the combobox
<TextBlock Text="State"/>
<ComboBox x:Name="cbState"/>
My C# code in the code-behind is as follows
private void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
List<ComboBoxItemClass> state_items = new List<ComboBoxItemClass>();
List<State> states = Location.GetStates();
foreach(State s in states)
{
ComboBoxItemClass item = new ComboBoxItemClass() { Text = s.State_Name, Value = s.State_Id };
state_items.Add(item);
}
cbState.ItemsSource = state_items;
cbState.SelectedValue = 3;
The combobox on running in emulator does not show the selected state. On clicking it shows the list of states.
On debugging the selectedvalue is shown to be null despite assigning it a value.
There is no problem with the rest of code and there exists a state with State_Id=3

I have solved this in two ways
The first way is to get the list of states in states variable. Assign this to ComboBox ItemSource. Then get the the State_Id and find the index of that particular state from the same states list and assign it to selected index.
Code Behind as Follows
states = Location.GetStates();
cbState.ItemsSource = states;
cbState.SelectedIndex = states.IndexOf(states.Where(x=>x.State_Id==State_Id).First());
Second method is as suggested in the comments section
states = Location.GetStates();
cbState.ItemsSource = states;
int index=states.IndexOf(states.Where(x=>x.State_Id==State_Id).First());
cbState.SelectedItem = states[index];
XAML is as follows
<ComboBox x:Name="cbState" >
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding State_Name}"></TextBlock>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Also I would like to post to my fellow WinRT developers that it is not required to create a separate class like ComboBoxItemClass like I did in the question to use combo box. Just get the list of your states, assign it to ItemSource and use any of the above methods.
Also if you want the State_Name and State_Id from the ComboBox you can do this.
State mystate=(State)ComboBox.SelectedItem;

Related

Get Element Root's ViewModel Context in WINUI3

I have a page which contains a ListView x:bound to an object in my ViewModel. This object contains a list of objects (timestamps) that contains a list of Subjects that contains a list of another objects. I'm presenting the data in 2 list views, one inside another.
<ListView
x:Name="primaryList" // for exemplification purposes
ItemsSource="{x:Bind ViewModel.VideoProject.TimeStamps, Mode=OneWay}"
ItemClick='{x:Bind ViewModel.ListViewTimeStamps_ItemClick, Mode=OneWay}'>
The ListView contains a DataTemplate for another ListView
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Spacing="5">
<TextBlock Text="{Binding Id}"
FontSize="15"
HorizontalAlignment="Left"
FontWeight="Bold" />
<ListView ItemsSource="{Binding Subjects}"
x:Name="secondaryList"
SelectionMode="Multiple">
<ListView.ItemTemplate>
....
And the second ListView is followed by another same structure.
My goal is to bind the second ListView ItemClickEvent to ListViewTimeStamps_ItemClick method inside my ViewModel, because I need the data contained in the object that secondaryListView holds (Subject).
I could try to set the Data Template Context to the ViewModel but it would break the Subject bind.
I found a ton of questions about this topic but differently from WPF there's not AncestorType to catch the up tree reference.
Obs:
My project is based on the Template Model which creates the XAML .cs with the ViewModel as a Property. I also haven't set the DataContext on the XAML page because I can x:bind normally my view model to the page elements without explicit set.
Is there a way to accomplish without using Attached Properties?
Thank you.
Since there is no support for setting the AncestorType property of a RelativeSource in WinUI, there is no way to accomplish this in pure XAML without writing some code.
You could implement an attached bevaiour as suggested and exemplified here:
public static class AncestorSource
{
public static readonly DependencyProperty AncestorTypeProperty =
DependencyProperty.RegisterAttached(
"AncestorType",
typeof(Type),
typeof(AncestorSource),
new PropertyMetadata(default(Type), OnAncestorTypeChanged)
);
public static void SetAncestorType(FrameworkElement element, Type value) =>
element.SetValue(AncestorTypeProperty, value);
public static Type GetAncestorType(FrameworkElement element) =>
(Type)element.GetValue(AncestorTypeProperty);
private static void OnAncestorTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement target = (FrameworkElement)d;
if (target.IsLoaded)
SetDataContext(target);
else
target.Loaded += OnTargetLoaded;
}
private static void OnTargetLoaded(object sender, RoutedEventArgs e)
{
FrameworkElement target = (FrameworkElement)sender;
target.Loaded -= OnTargetLoaded;
SetDataContext(target);
}
private static void SetDataContext(FrameworkElement target)
{
Type ancestorType = GetAncestorType(target);
if (ancestorType != null)
target.DataContext = FindParent(target, ancestorType);
}
private static object FindParent(DependencyObject dependencyObject, Type ancestorType)
{
DependencyObject parent = VisualTreeHelper.GetParent(dependencyObject);
if (parent == null)
return null;
if (ancestorType.IsAssignableFrom(parent.GetType()))
return parent;
return FindParent(parent, ancestorType);
}
}
So no replacement so far for AncestorType?
No. Not in XAML.

How to bind a ReactiveCommand to a control in a Xamarin.Forms ListView?

I am using ReactiveUI, Xamarin.Forms and XAML. I am trying to implement a simple scenario with a ListView where each row has a delete button. Here is the ListView XAML:
<ListView x:Name="playerListView" ItemsSource="{Binding Players}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal" Padding="20, 5, 20, 5">
<Label Text="{Binding .}" VerticalOptions="CenterAndExpand" HorizontalOptions="StartAndExpand" />
<Button x:Name="deleteButton" Text="Delete" Clicked="onDeleteClicked" VerticalOptions="CenterAndExpand" HorizontalOptions="EndAndExpand" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
As you can see, the delete button has a Clicked handler registered. This works but it does not feel like the RxUI way. Here is the code behind:
private void onDeleteClicked(object sender, EventArgs e)
{
var button = (Button)sender;
this.ViewModel.RemovePlayer.Execute(button.BindingContext);
}
How can I replace this onDeleteClicked event handler with a declarative binding to my RemovePlayer command? I can't see a good way to do it because I chose to bind the ListView to a ReactiveList<string>, so if I try to do Command="{Binding RemovePlayer}" it fails because the cell is bound to a string.
For completeness here is my view model:
public class NewGameViewModel : ReactiveObject
{
public ReactiveList<string> Players { get; private set; }
public ReactiveCommand<Object> AddPlayer { get; private set; }
public ReactiveCommand<Object> RemovePlayer { get; private set; }
public ReactiveCommand<Object> StartGame { get; private set; }
public ReactiveCommand<Object> RandomizeOrder { get; private set; }
string newPlayerName;
public string NewPlayerName {
get { return newPlayerName; }
set { this.RaiseAndSetIfChanged(ref newPlayerName, value); }
}
public NewGameViewModel()
{
Players = new ReactiveList<string> ();
var canStart = this.Players.CountChanged.Select(count => count >= 3);
StartGame = canStart.ToCommand();
RandomizeOrder = canStart.ToCommand();
AddPlayer = this.WhenAnyValue(x => x.Players.Count, x => x.NewPlayerName,
(count, newPlayerName) => count < 7 && !string.IsNullOrWhiteSpace(newPlayerName) && !this.Players.Contains(newPlayerName))
.ToCommand();
RemovePlayer = ReactiveCommand.Create();
}
}
Because there is no relative binding support in Xamarin Forms at the moment (see this Xamarin Forms forums post for more info), you won't be able to bind a command to your Button within your ListViews DataTemplate. Any binding within that DataTemplate will have a BindingContext relative to the current item in the list - in your case, a simple string. If your ListView was bound to an object, let's say a Person, then your Button command binding would still fail with an error something along the lines of No Command RemovePlayer found on object Person
So implementing the Command in the view's code behind like you have done is one option. Another is using a C# DataTemplate (not a XAML one) and implementing the Command there - but both of those are kind of the same thing. Neither are a great solution if you like keeping stuff like that out of your views and only within your view models; but until relative binding support is introduced there aren't really any other options.
I ran into the exact same problem as you, but I was binding my ListView to a collection of objects. The class for my object was in a separate class library that ONLY has POCOs in it, and I did not like the idea of implementing a Command within one of my POCO's.
The tricky bit is that your "SelectedPlayer" isn't exposed in your ViewModel, so there's no way to do this the RxUI way. If it was, you can do something like:
RemovePlayer.Select(_ => SelectedPlayer).Subscribe(x => {
SelectedPlayer = null;
Players.Remove(x);
});
If your Player object was itself a ViewModel and "RemovePlayer" was on the Player itself, you can do this Tricky Trick:
Players.Changed.StartWith(null)
.Select(_ => Players
.Select(x => x.RemovePlayer.Select(__ => x))
.Merge())
.Switch()
.Subscribe(x => Players.Remove(x));
Here, we're saying, "Every time the Players list changes, I want to build a new Observable: Take the list of all the current players, and Select them into an Observable that fires when someone hits a RemovePlayer button - tell me when any of those new Observables fire"
My opinion on this is don't be a purist :)
Is not the best design model, but extend your object to handle a ICommand and bind to it.. it's the best solution for now imo.
If your objects are on the same assembly you can use partial's , if are different , you can create small viewmodel for your poco .

Command parameter in EventTrigger

I'm working on WinRT with MvmmCross v3 framework and Windows.UI.Interactivity.
I want a TextBox with an EventTrigger on the event TextChanged which launch a Command. And also, I want in CommandParameter the text of the textBox.
So I have this code
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding UpdateText}" CommandParameter="{Binding Text}"/>
</i:EventTrigger>
public ICommand UpdateText
{
get
{
return new MvxCommand<object>((textSearch) =>
{
// code...
});
}
}
But my textSearch parameter equals to "{Windows.UI.Xaml.Controls.TextChangedEventArgs}" with all of these properties NULL.
I Tried also to declare explicitly my ElementName in the binding like
CommandParameter="{Binding Path=Text, ElementName=tes}
But it failed too.
Thanks
Do you really need to handle TextChanged event? You could be notified of the changes by just binding to the Text property:
<TextBox Text="{Binding TextValue, Mode=TwoWay}" />
And then in the view model:
private string _textValue;
public string TextValue
{
get
{
return _textValue;
}
set
{
if (_textValue == value)
return;
_textValue = value;
OnTextChanged(value); // react to the changed value
}
}
EDIT:
There are two things you need to be aware of, if you want to get to the Text value from inside your Command:
First, you need to fix the CommandParameter binding. By using {Binding Text} you are actually trying to bind to a property in your view model, i.e. you would first need to bind the TextBox.Text property to the same view model property. As you've said in the comment, that's no good for you because you need the info on every change and not only on lost focus, so the value you get is not up to date enough.
The right approach would therefore be your second attempt, i.e. binding directly to the TextBox using the ElementName syntax. Unfortunately triggers are not a part of the visual tree therefore they don't get access to the XAML name scope (you can read more about it in this blog post). You can work around that by using NameScopeBinding from MVVMHelpers.Metro package:
<Behaviors:NameScopeBinding x:Key="MyTextBox"
Source="{Binding ElementName=MyTextBox}" />
Now you can make the ElementName binding work:
<i:InvokeCommandAction Command="{Binding UpdateText}"
CommandParameter="{Binding Source.Text, Source={StaticResource MyTextBox}}"/>
You still have the second problem. The Text value that you are binding to only updates on lost focus so you don't get the right value when handling TextChanged event. The solution is to bind to the TextBox itself:
<i:InvokeCommandAction Command="{Binding UpdateText}"
CommandParameter="{Binding Source, Source={StaticResource MyTextBox}}"/>
And then inside the command get the Text property directly from the TextBox:
private void OnUpdateText(object parameter)
{
var value = ((TextBox) parameter).Text;
// process the Text value as you need to.
}
EDIT 2:
To make the above code work with the view model being in a PCL, there a couple of approaches you could take.
The simplest hack would be to use reflection. Since it is available in PCL you could get to the Text property and read its value:
private void OnUpdateText(object parameter)
{
var textProperty = textSearch.GetType().GetProperty("Text");
var value = textProperty.GetValue(parameter, null) as string;
// process the Text value as you need to.
}
A cleaner solution would be to put the WinRT specific code into a separate assembly abstracted via an interface. You would first create an interface in the PCL library:
public interface IUiService
{
string GetTextBoxText(object textBox);
}
And modify view model to accept this interface in the constructor:
public ViewModel(IUiService uiService)
{
_uiService = uiService;
}
In the command handler you would than use the method in the interface:
private void OnUpdateText(object parameter)
{
var value = _uiService.GetTextBoxText(parameter);
// process the Text value as you need to.
}
You would implement that interface in a WinRT library:
public class UiService : IUiService
{
public string GetTextBoxText(object textBox)
{
var typedTextBox = textBox as TextBox;
return typedTextBox.text;
}
}
In the application you reference this library and pass the implementation to view model:
var viewModel = new ViewModel(new UiService);
My favorite approach is different: I'd create a Behavior exposing a Text property automatically updated every time TextChanged event is triggered:
public class TextChangedBehavior : Behavior<TextBox>
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(TextChangedBehavior),
new PropertyMetadata(null));
public string Text
{
get { return (string) GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.TextChanged += OnTextChanged;
Text = AssociatedObject.Text;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.TextChanged -= OnTextChanged;
}
private void OnTextChanged(object sender, TextChangedEventArgs textChangedEventArgs)
{
Text = AssociatedObject.Text;
}
}
Now I could bind a TextValue property to this behavior and react to its change in the property setter as already suggested at the very beginning of this long answer:
<TextBox>
<i:Interaction.Behaviors>
<b:TextChangedBehavior Text="{Binding TextValue, Mode=TwoWay}" />
</i:Interaction.Behaviors>
</TextBox>

Silverlight 5: Binding command to listboxitem

I am beginner in silverlight and all this mvvm pattern is bit confusing.
In my application I have two listboxs one for country and one for states.
What I want to do is when I select a Country from the listbox1 second listbox will display states from the selected country.
i.e I want to bind command in xaml to listboxitem.
I try to find the solution by Google but either solutions was too complex for me to understand or using different mvvm pattern like prism,light etc.
There are a few different ways of doing this:
1: (Easiest!) Bind the SelectedItem of the first ListBox to your ViewModel. In the Setter for the ViewModel property, change the list that you're binding to the second listbox. Note that your ViewModel property will need to use INotifyPropertyChanged to notify that the list has changed.
Eg: If your xaml looks like:
<ListBox ItemSource="{Binding ListOne}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}"/>
<ListBox ItemSource="{Binding ListTwo}"/>
Then your ViewModel might be a bit like:
public List<MyItem> ListOne { get; set; }
private MyItem _selectedItem
public MyItem SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
ListTwo = _selectedItem.SubItems;
}
}
private List<MyOtherItem> _listTwo
public List<MyOtherItem> ListTwo
{
get { return _listTwo; }
set
{
_listTwo = value;
RaisePropertyChanged("ListTwo");
}
}
2: If the data for the second list is literally a property of the items in the first list, you can use an Binding in xaml to directly join them up.
ItemSource="{Binding Path=SelectedItem.MyItemsProperty, ElementName=MyFirstListBoxName}"
3: You can use an EventTrigger with an EventToCommand to turn the SelectedItemChanged event into a Command execution. You're not literally binding a command to the ListBoxItem, you're binding the command to the change.
I would recommend the first option, it's easiest and gives you good control of what's going on without getting too complicated.

Silverlight: How do I bind generic list data to combobox?

Please forgive, I'm new to Silverlight and am still trying to wrap my head around data binding...
I have a generic list obtained from a class using LINQ. The list has 4 objects, each object consisting of a Letter property (string - A, B, C and D) and a corresponding Number property (integer - 1, 2, 3, and 4).
In Silverlight, I have a combobox control and a text block. I'm trying to figure out how to:
Bind the combobox to the generic list so that the letters populate the combobox
When the user selects a letter in the combobox (say C), the corresponding integer value (3 for this example) is displayed in the text block.
I'm been trying to make it work with ItemsSource, but am not getting anywhere. Any advice? I'm working in VB, by the way...
Thanks
I did something similar with a Label and a radComboBox (from Telerik).
If you want to do this by code, you'll have to do it like this:
'This code is untested but at least it shows the logic
'Step #1, filling the combobox
yourComboBox.ItemsSource = yourList
yourComboBox.SelectedValuePath = "IntegerPropertyName"
yourComboBox.DisplayMemberPath = "StringPropertyName"
'Step #2, binding the TextBlock
Dim binding As System.Windows.Data.Binding = Nothing
binding = New System.Windows.Data.Binding()
binding.Source = yourComboBox
binding.Mode = Data.BindingMode.TwoWay 'Maybe in your case you'll want OneWay
binding.Path = New System.Windows.PropertyPath("SelectedItem.IntegerPropertyName")
youtTextBlock.SetBinding(TextBlock.TextProperty, binding)
... and if you want to do it directly in the XAML, have a look at this post for step #2
I would do this in XAML only. Here is my (Sample) Code:
<Grid x:Name="LayoutRoot" Background="White">
<TextBlock Text="{Binding ElementName=MyComboBox, Path=SelectedValue}" VerticalAlignment="Top"/>
<ComboBox
x:Name="MyComboBox"
ItemsSource="{Binding MyColl}"
Height="22"
SelectedValuePath="I"
DisplayMemberPath="C"/>
</Grid>
And here is my Code behind: (EDIT: sry for the c# code)
public class MyClass
{
public int I { get; set; }
public string C { get; set; }
}
public partial class MainPage : UserControl
{
public ObservableCollection<MyClass> MyColl { get; set; }
public MainPage()
{
MyColl = new ObservableCollection<MyClass>();
MyColl.Add(new MyClass{ C = "A", I = 1});
MyColl.Add(new MyClass { C = "B", I = 2 });
MyColl.Add(new MyClass { C = "C", I = 3 });
MyColl.Add(new MyClass { C = "D", I = 4 });
DataContext = this;
InitializeComponent();
}
}
Remember: This is just a sample code. I strongly recommend you to hav a look at MVVM (http://jesseliberty.com/2010/05/08/mvvm-its-not-kool-aid-3/). A better Solution would be, to bind the SelectedItem (or the selected value) to your ViewModel, and then reference this value in the TextBlock.
BR,
TJ