Command parameter in EventTrigger - xaml

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>

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.

UWP "compound" binding (binding Data.Name instead of Name)

I have a simple page with text block and a button. I want to change the text when I press the button. But using a text from a Data.Name property of the Page.
I know I can have this simpler (having just Name instead of Data.Name), but I need Data.Name, don't ask why.
For this I have a class DataType which has the Name property and object named Data of that class. I want to have Data inside this Page, and bind the text to the Data.Name property.
When I click on the button, nothing happens, the question is how canI make this work?
XAML:
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="auto" />
<RowDefinition Height="auto" />
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="{Binding Data.Name, Mode=OneWay}"/>
<Button Grid.Row="1" Content="Change" Click="Button_Click"/>
</Grid>
</Page>
Class DataType
public ref class DataType: public INotifyPropertyChanged {
public:
property String^ Name
{
String^ get() {
return m_Name;
}
void set(String^ value) {
m_Name = value;
OnPropertyChanged("Name");
}
}
virtual event PropertyChangedEventHandler^ PropertyChanged;
private:
void OnPropertyChanged(Platform::String^ propertyName)
{
PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));
}
String^ m_Name;
};
Class MainPage
public ref class MainPage sealed: public INotifyPropertyChanged
{
public:
MainPage();
property DataType^ Data {
DataType^ get() {
return m_Data;
}
void set(DataType^ value) {
m_Data = value;
OnPropertyChanged("Data");
}
}
virtual event PropertyChangedEventHandler^ PropertyChanged;
private:
void OnPropertyChanged(Platform::String^ propertyName)
{
PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName));
}
void Button_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
Data->Name = rand() & 1 ? "Test1" : "Test2";
OnPropertyChanged("Data");
}
DataType^ m_Data;
};
In UWP, there are x:Bind and Binding markup extension, they have some differences when you use them. You can learn the details from the document of above two links.
Now we will discuss the reason that caused your above issue.
In your xaml, you use the Binding markup extension to bind the property path, since Binding uses the DataContext as a default source. Simplely to say, when you use Binding property path, you bind the DataContext.Property path, you just need use the Bind source object's property but not need to specify the Source data object on the xaml. As the introduction of Traversing an object graph:
"{Binding Path=Customer.Address.StreetAddress1}"
Here's how this path is evaluated:
The data context object (or a Source specified by the same Binding) is searched for a property named "Customer".
The object that is the value of the "Customer" property is searched for a property named "Address".
The object that is the value of the "Address" property is searched for a property named "StreetAddress1".
See the Property-path syntax for the details.
So your code will work just binding the Name property and set the DataContext. (Note that: your MainPage class don't need to implement the INotifyPropertyChanged interface.)
<TextBlock Grid.Row="0" Text="{Binding Name, Mode=OneWay}"/
And
this->DataContext = Data;
Also note: If you're using Visual C++ component extensions (C++/CX) then, because we'll be using {Binding}, you'll need to add the BindableAttribute attribute to the DataType class.
[Windows::UI::Xaml::Data::Bindable]
public ref class DataType sealed : public INotifyPropertyChanged {
...
}
On the other hand, you can use the x:Bind instead of the Binding, since x:Bind don't use the DataContext as a default source—instead, it uses the page or user control itself. So it will look in the code-behind of your page or user control for properties, fields, and methods. To expose your view model to {x:Bind}, you will typically want to add new fields or properties to the code behind for your page or user control. For example: in a page, Text="{x:Bind Employee.FirstName}" will look for an Employee member on the page and then a FirstName member on the object returned by Employee.
The issue is that DataType properties isn't visible from XAML, because "DataType.h" isn't included in "pch.h"
Once I included it in precompiled header, everything worked.
BTW, looks like Binding is not checking for type visibility from XAML or whatever, but x:Bind does. Using x:Bind, the compiler complains about unknown Data.Name, and that allowed me to figure out the problem.

Issues with ContentDialog and x:Bind

I have a ContentDialog that implements INotifyPropertyChanged. I have a TextBox with its Text property bound via x:Bind syntax to a string property on my code-behind.
If I edit the contents of the TextBox, and then click on another control in the ContentDialog (losing focus on the TextBox), and then click on the primary button, by the time I get to the primary button click event handler the text property has been updated with the content of the TextBox control = PERFECT.
However, if I change the contents of the TextBox but then keep it in focus and THEN click on the primary button the binding never gets updated.
It seems that the reason for this is because the built-in buttons in ContentDialog do not acquire focus before their click eventhandlers are fired, and as x:Bind only supports LostFocus binding, these bindings will never be updated.
I'm staggered that this big bug exists. So my 2 questions are
1) Is there a workaround
2) Do I have to abandon x:Bind and use WPF-style Binding syntax, where I can change the UpdateSourceTrigger within the binding itself.
I'm hoping that another UWP dev has come across this and knows of a work around
EDIT
Ive created some example code to demonstrate the issue.
page:
<Page
x:Class="App1.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:App1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Button Content="Open" Click="OpenClick" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Page>
code behind:
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
}
private async void OpenClick(object sender, RoutedEventArgs e)
{
var dialog = new ContentDialog1();
await dialog.ShowAsync();
}
}
content dialog:
<ContentDialog
x:Class="App1.ContentDialog1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="TITLE"
PrimaryButtonText="Button1"
PrimaryButtonClick="ContentDialog_PrimaryButtonClick">
<Grid>
<TextBox Text="{x:Bind Path=TestText, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center" MinWidth="100"/>
</Grid>
</ContentDialog>
content dialog code behind:
public sealed partial class ContentDialog1 : ContentDialog, INotifyPropertyChanged
{
public ContentDialog1()
{
InitializeComponent();
}
private string _testText;
public string TestText
{
get => _testText;
set
{
_testText = value;
OnPropertyChanged(nameof(TestText));
}
}
private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
And my target sdk verions are:
If you click open the content dialog, enter some text, then click the primary button - if you have a breakpoint in the click handler you'll see that the binding hasn't updated with your entered text
OK, I've looked into this, and have tested this on all SDK's back to build 10586. Essentially ContentDialog is broken for x:Bind bindings to TextBox, because TextBox is required to lose focus in order for its bindings to update.
I have tried a number of hacks including walking the visual tree on Loaded to add extra handlers to the hidden buttons, and trying to force them to focus (thus removing focus from any TextBox, and triggering the binding update) before the click handler fires, but without UWP supporting the tunnelling event strategy (like WPF), it is not possible to inject any kind of event handling between the button press and the hard-wired click handler running.
The upshot is that x:Bind (which doesn't support UpdateSourceTrigger other than LostFocus) cannot reliably work in ContentDialog with TextBox controls.
I have logged a bug at Microsoft uservoice.
The only way to solve this is to abandon x:Bind and use classic Bindings which support an UpdateSourceTrigger of PropertyChanged - thus allowing bindings to be always up-to-date before the button in clicked.
But even this is a hack, because UpdateSourceTrigger of PropertyChanged will cause unnecessary updates in your setters - which can be problematic. Also updating setters in INotifyPropertyChanged will fire many additional rounds of the arrange/measure pass for all of your layout.
The only real solution is to write your own ContentDialog control. I was tempted to do this, but without access to Microsofts source code for these controls, it's difficult to know if you have done it successfully
I can't reproduce the issue: the setter of string property in code-behind is always fired before the ContentDialog_PrimaryButtonClick, even if I click the primary button while the focus is still on the TextBox. My sample (tested on Fall Creators Update):
MyContentDialog.xaml
<Grid>
<TextBox Text="{x:Bind MyString, Mode=TwoWay}"/>
</Grid>
MyContentDialog.xaml.cs
public sealed partial class MyContentDialog : ContentDialog, INotifyPropertyChanged
{
private DTO dto;
private string myString;
public string MyString
{
get
{
return myString;
}
set
{
myString = value;
NotifyPropertyChanged(nameof(MyString));
}
}
public MyContentDialog(DTO dto)
{
this.InitializeComponent();
this.dto = dto;
MyString = dto.text;
}
private void ContentDialog_PrimaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
dto.text = MyString;
}
private void ContentDialog_SecondaryButtonClick(ContentDialog sender, ContentDialogButtonClickEventArgs args)
{
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
DTO.cs
public class DTO
{
public string text;
}
MainPage.xaml.cs
private async void Page_Loaded(object sender, RoutedEventArgs e)
{
DTO dto = new DTO { text = "My text" };
MyContentDialog dialog = new MyContentDialog(dto);
await dialog.ShowAsync();
await new MessageDialog("My text, after being edited by the user in MyContentDialog: " + dto.text).ShowAsync();
}
Could you share your code? What's the min and target version of your project?
I just figured it out I spent a lot of time just like you did almost two days failed and Content Dialog crashes my application if there is null object binding to any of the control have to use Converters.
I was just thinking how about placing a usercontrol on the dialog and see if the binding works.
Let me try post the results here.
If you have figured out any other way of doing this let me know

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 .

Problems with SelectedValue, SelectedIndex while data binding to a ComboBox c++/cx XAML Metro app

How can I use the value of the ComboBox's selected element in the following code?
C++:
namespace testtesttest
{
[Windows::UI::Xaml::Data::Bindable]
public ref class Wrapper sealed : Windows::UI::Xaml::Data::INotifyPropertyChanged
{
public:
Wrapper()
{
// the index of the selected element of the combobox when the application starts
m_selectedElement = 2;
m_myStringArray = ref new Platform::Collections::Vector<int>(3);
// 1, 2, and 4 in the combobox list
m_myStringArray->SetAt(0,1);
m_myStringArray->SetAt(1,2);
m_myStringArray->SetAt(2,4);
}
virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged;
property Windows::Foundation::Collections::IVector<int>^ MyStringArray
{
Windows::Foundation::Collections::IVector<int>^ get() { return m_myStringArray; }
}
property int SelectedElement
{
int get() { return m_selectedElement; }
void set(int value) { m_selectedElement = value; RaisePropertyChanged("SelectedElement"); }
}
protected:
void RaisePropertyChanged(Platform::String^ propertyName)
{
PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(propertyName));
}
private:
Platform::Collections::Vector<int>^ m_myStringArray;
int m_selectedElement;
};
}
XAML:
<TextBlock HorizontalAlignment="Left"
Height="73" Margin="50,436,0,0"
TextWrapping="Wrap"
Text="{Binding Path=SelectedElement}"
VerticalAlignment="Top"
Width="200"/>
<ComboBox ItemsSource="{Binding Path=MyStringArray}"
SelectedIndex="{Binding Path=SelectedElement}"
HorizontalAlignment="Left"
Height="50" Margin="369,50,0,0"
VerticalAlignment="Top" Width="286"/>
I tested other bindings and they worked. I am setting the DataContext right.
The m_selectedElement = 2 in the constructor sets the selected element in the combobox to the 3rd in the list. The get() method of the SelectedElement property gets called, but the set() method doesn't. I checked this by placing a breakpoint. What am I doing wrong?
Also, is it possible to bind a Platform::Array^ to a ComboBox?
I tried using Platform::Array < Platform::String ^>^ and also Platform::Array < int>^ and I couldn't get it work. STL containers also didn't work. What are the other possible containers that can bind to a combobox?
Change
SelectedIndex="{Binding Path=SelectedElement}"
to
SelectedIndex="{Binding Path=SelectedElement, Mode=TwoWay}"
You need a two-way binding if you want the UI to update your ViewModel.
You can only use WinRT components in bindings (ref classes/structs, enum classes). Using Platform::Collections::Vector is generally the right choice when it's used for binding, especially because it also implements IObservableVector. STL containers don't work because they cannot travel across the ABI.