Firstly, my apologies to any one who may feel like this is a very dumb question or a question that has already been answered. I'm very new to MVVM, XAML and having read everything on StackOverflow, I'm still failing to understand what I'm sure is a simple concept.
So I have a MVVM set up where an Observable Collection is put into a ComboBox. Here is the view.
public class AppSettings : INotifyPropertyChanged
{
public class Regions
{
public int code { get; set; }
public string region { get; set; }
}
private ObservableCollection<Regions> _ItemsRegions = new ObservableCollection<Regions>
{
new Regions() { code = 0, region = "Metro : All Areas" },
new Regions() { code = 25, region = "Metro : North of River"},
new Regions() { code = 26, region = "Metro : South of River"},
new Regions() { code = 27, region = "Metro : East/Hills"},
};
public ObservableCollection<Regions> Region
{
get { return this._ItemsRegions; }
}
private Regions _selectedRegion;
public Regions SelectedRegion
{
get { return _selectedRegion; }
set
{
if (value != _selectedRegion)
{
_selectedRegion = value;
RaisePropertyChanged("SelectedRegion");
}
}
}
And for reference, here is my ComboBox.
<ComboBox Name="Regions" ItemsSource="{Binding Region}" DisplayMemberPath="region" SelectedItem="{Binding SelectedRegion, Mode=TwoWay}"/>
Now the good news is that I have the SelectedItem working and I'm able to with the MVVM obtain the values I need to make changes etc. which is great! That concept was a little difficult to grasp at first but once I had it, I knew what was going on. So I can save the data in any way at this point.... The eventual plan is to put it in roaming data. This is a Universal App.
So the problem is the user is finished with this page and navigates away from it . When he/she comes back to it the ComboBox is loaded with all the data (as expected) but the SelectedItem that I just saved is not. As in, the "IsSelected = True" item is null so it just says "choose an item".
What do I need to do in order to on page load, get the SelectedItem from save and set it to the ComboBox. I've been stupid and tried setting the Region.ComboBox.SelectedItem to a "string", but of course that defeats and destroys the whole MVVM purpose!
So, what do I need to do? I'm happy to be just pointed at some documentation for me to read.
Update Edit
So following advise from a user on here I took my get; set INotifyPropertyChanged logic and put it in a blank app. Just to see that I'm not doing anything stupid.
Unfortunately the blank app also has the same problem where navigation away from the page and back to it has none of my previously selected values... To illustrate this, here is a quick screenshot.
To navigate back, I'm using the hardware back button.
I'm guessing that I'm just missing a piece of the puzzle to make this work. This whole MVVM XAML concept is new to me and while I'm learning a lot about it, I'm genuinely stuck on this!
Any takers? Tips? Ideas? Examples?
Update 2
Okay so following some more advise posted in the comments on this question, I went and added a debugger track on the Object ID for "this._selectedRegion".
The watch of the Object ID shows that the data is in fact set correctly when a user selects one of the items from the combobox. Great! But when the user navigates away from the page and comes back to it, it's set to null. Not Great!
With my bland/stupid face am I missing the point completely? Am I meant to be on "OnNavigatedTo" aka, fist load setting the SelectedItem for the combobox myself? As in the data is never saved on navigation away and coming back to it is setting is null is in fact expected in MVVM? Do I manipulate the "get" statement and specify the ObservableCollection Item it's meant to be? Crude way, but like
_selectedRegion = something
return _selectedRegion
This sounds awfully crude to me and not what it should be, but maybe I'm just not appreciating the MVVM set up properly?
Thoughts anyone?
For a ComboBox in WPF it is very important to set SelectedItem to one of the items in ItemsSource.
Make sure that SelectedItem has the correct value (or is set) when he/she comes back.
Edit
Your binding
SelectedItem="{Binding SelectedRegion, Mode=TwoWay}"
will call the getter of SelectedRegion when the control is built. It will call the setter when the value in the ComboBox changes.
Related
I'm working on an app that has a "day" and "night" color palette that can change automatically. We're using Xamarin Forms and, for historical reasons, we're not using XAML but I speak XAML so I'm going to use it in this post.
I've approached it by creating a base type with a property for relevant colors like "dark text" or "header background", then implementing that type for both schemes. Then, I made a type that references one of those and raises a PropertyChanged even if it changes. So a day->night transition involves setting the property, then anything in the UI bound to a path like "ColorScheme.DarkText" changes. Nice.
Now I've got a ViewModel that wants to have different colors for some items in a list. I want those colors backed by this day/night change system, but I might have designed myself into a corner. I'll show you what I did and how I want to redesign, but I'm curious if there's a clever way to go about it without causing other problems.
Here's a VM for an item I'm binding to, let's all assume there's nothing unexpected in ViewModelBase:
public class ItemViewModel : ViewModelBase {
public string IconColorName { get...; set...; }
public string IconText { get...; set...; }
}
That ViewModel's contained in another boring ViewModel that makes up the rest of the page:
public class PageViewModel : ViewModelBase {
public ObservableColorScheme ColorScheme { get...; set...; }
public ObservableCollection<ItemViewModel> Items { get...; set...; }
}
OK, so what I'm going for is I'd like XAML for my item's template to look something like:
<StackLayout>
<StackLayout.Children>
<Label TextColor={Binding IconColor, Converter={StaticResource StringToColorConverter} />
...
</StackLayout.Children>
</StackLayout>
Right. OK. So now here's the problem. I can imagine building that IValueConverter and setting it up so it has the same concept of the right color scheme, then using the string value here to get the appropriate property. But I have a problem: there can only be one source for a binding, right? I need the color to change if EITHER the ColorScheme or IconColorName changes. My hunch is WPF could do that, but Xamarin can't?
The most obvious solution I've thought of is some kind of extra ViewModel, in XAML-unfriendly format for brevity:
public class ColorViewModel : ViewModelBase {
public Color Color { get...; set...; }
public ColorViewModel(string colorName, ObservableColorScheme colorScheme) {
colorScheme.PropertyChanged += (s, e) => {
if (e.PropertyName == colorName) {
Color = colorScheme.Get(colorName);
}
}
Color = colorScheme.Get(colorName);
}
}
I do NOT like this. These items are created and destroyed a lot, so that means that event handler needs to be unsubscribed. I don't want to have to think about that, and I can assume a maintenance programmer will forget. I've thought about retooling it to use a WeakReference for the event subscription but... that's getting really icky.
So I'm not really sure how to proceed, short of making the Page here detect color scheme changes and manually update its child views. That feels icky too. I've been thinking about it for a couple of days and nothing nice is presenting itself.
I'm open to "you're doing this terribly wrong, and there's some feature that would make this dramatically easier for you". I'm suspicious that feature is Styles, which I'm not using because 1) the aforementioned lack of using XAML and 2) our project is older than Style support in Xamarin Forms. Feel free to tell me to throw this design away, but please don't do so without showing me a quick example of the better way!
I don’t know xamarin nor C#, so maybe I’ll use the wrong terms, but this is how I would approach this problem:
I assume you have some kind of ViewModel hierarchy and you know the root of this hierarchy.
I would create a ColorScheme class with a static getter of the current color scheme and getters for each color. So you can create a subclass for day and night scheme. Also a void ApplyTo(ViewModelBase). I would create an ViewModelBase interface which has an void UpdateColorScheme() and List Items().
Ok, now, each ViewModel can use the UpdateColorScheme method to setup the view will be created.
Probably there is some kind of event to change the color scheme. This could be a button or a clock based trigger. This event sets the correct ColorScheme and simply calls ColorScheme.CurrentScheme().ApplyTo(rootViewModel). The ApplyTo method walks down the ViewModel hierarchy and calls UpdateColorScheme() for each ViewModel.
This isn’t very fancy but your don’t have to create dozens of objects for something which will change rarely (only twice a day). You only have one instance of ColorScheme and be instance for each Color and a separate method for color settings. But you don’t have to create and register events listeners all the time. The base class will enforce the maintenance programmer to use this concept so he can not forget to setup a event listener and you use the same code for setting up and updating the view.
And a little side note: opinion based questions are not allowed on Stack Overflow. There is a Core Review Page of StackOverflow, I think this question belongs there.
I am developing a windows mobile application with Xamarin, but don't seem to be able to programmatically set the selected ListView item.
I have tried the following ListViews methods and still nothing
SelectedItem
ScrollTo()
Focus()
I have also googled it and can't seem anything to say how to do this.
How do I do this?
This works fine in my sample app:
public partial class ItemsPage : ContentPage
{
public ItemsPage()
{
InitializeComponent();
Vm = new ItemsViewModel();
BindingContext = Vm;
}
protected override void OnAppearing()
{
ListviewItems.SelectedItem = Vm.Items[1];
}
public ItemsViewModel Vm { get; private set; }
In my sample app, ItemsViewModel.Items is a List<string>.
The second item in the list is set selected after this line of code runs in OnAppearing.
setting the SelectedItem property is the "correct" way to do it. What specifically is not happening that you think you happen when it is set?
I used a method but it may not be the most effective one. You can set your change to the list which was created by your model type and then you should reload the list view.
subjects[i].something=false;//set something
yourListView.ItemSource=null;
yourListView.ItemSource=subjects;
if you want to scroll to the specific location,
I am using
listChat.SetSelection(currentIndex);
in one of my chat application and it works fine.
if you are looking for the solution to scroll to desired position, even I have searched for the solution on internet, nothing helped. finally this one was my work around to make it work.
Edit: i have simplified this problem a lot since i first wrote it up. See the code examples at the bottom. I will clean the post up tomorrow.
I want to set the DataContext AFTER the user as filled out a form because that is when I know the type of object I need to create (based on what the user selected). The problem with doing it this way is that the BindingGroup.IsDirty flag is false because all edits are done by this point and I suppose that is why my call to BindingGroup.UpdateSources is having no effect.
Essentially this is what I'm doing:
Have user fill out form. The DataContext has not been set yet, but bindings are in place.
In the buttonSave_Click handler, create and set the correct DataContext object (based on provided user input) then call BindingGroup.UpdateSources and close the dialog.
I realize there are other ways to go about this. Probably even better ones, but I am really just experimenting with DataBinding and trying to learn it better. That is why I am trying out many possible designs with this.
Ultimately I will probably settle on a design where I ask the questions that I need answered in order to know what object to create for the data context then set it and let them fill out the remainder of the form from there. But for now I want to get it working the other way (if it is even possible) just for learning purposes.
EDIT 1:
I have determined that the IsDirty flag must not be my problem because immediately before the DataContext is set, IsDirty is true. Then immediately after DataContext is set it becomes false so I suppose it automatically updated sources then, however, I do not see the changes reflected in my data object so it obviously failed for some reason.
I know my Bindings are correct because if I set the DataContext in the Windows ctor it updates the data. Move those two lines of code to the buttonSave_Click handler and it no longer updates data.
I feel like there is something that I am missing that is going to me make me feel reaaallly stupid when I figure it out :\
UPDATE 1:
I have determined that the BindingExpression.Worker.CanUpdate is false. By looking at the source code of BindingExpression I can see that that would cause it to fail. Now to figure out why that is false....
UPDATE 2:
With all the reading i have been doing i am starting to suspect a timing issue. read this
I will be able to try it when i get home
UPDATE 3:
Still working on this. Here is a bit of code. I have simplified to get minimal code.
Here is the data objects.
public abstract class Dog
{
public Dog(SexType sex)
{
NickName = string.Empty;
}
public string NickName { get; set; }
}
public class Stud : Dog
{
public Stud()
: base(SexType.Male)
{
}
}
This code works:
//code behind
public partial class AddDogWindow : Window
{
public Dog NewDog { get; set; }
public AddDogWindow()
{
InitializeComponent();
//set the DataContext here and the data object will be updated as expected...
NewDog = new Stud();
DataContext = NewDog;
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
}
}
And the XAML
<Window x:Class="PuppyMan.AddDogWindow"
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"
xmlns:local="clr-namespace:PuppyMan"
mc:Ignorable="d"
Title="AddStudDogWindow" Height="300" Width="300">
<StackPanel>
<TextBox HorizontalAlignment="Stretch" Text="{Binding NickName, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<Button x:Name="buttonSave" Click="buttonSave_Click">Save</Button>
</StackPanel>
</Window>
THIS is the code that BREAKS.
public partial class AddDogWindow : Window
{
public Dog NewDog { get; set; }
public AddDogWindow()
{
InitializeComponent();
}
private void buttonSave_Click(object sender, RoutedEventArgs e)
{
//Notice all I have changed is moving the set DataContext out of the ctor and into this handler.
//Now the data object no longer gets updated.
NewDog = new Stud();
DataContext = NewDog;
DialogResult = true;
}
}
The idea is that I let them fully fill out the dog info and then I only create the Stud or Dame dog object based on the final decision of whether the dog is male or female. A kind of lazy data binding I suppose. I know this isn't the best design for this situation but I want to learn how to do it this way for learning purposes. Or learn why this method won't work, either is fine, I just want to learn this data binding stuff and well!
FINAL UPDATE
This post seems to be essentially the same as my problem. I am using OneWayToSource and it always sets my NickName property to the default value (in this case ""). The problem is NOT that the NickName getter is called after the setter, but that it gets set to "". The UI keeps the original value until I type in the TextBox again and INotifyPropertyChange fires then everything syncs up again.
Seems an awful lot like a bug to me that OneWayToSource would push a "" instead of the current value of the target. But I very well may be miss understanding.
Design your form such that you know beforehand which object you will get.
This can probably be done for example by checking if uses fills a particular field, or if he selects some combobox value in your form. In appropriate event of that control you can set your datacontext. And by the way Triggers are most suited here. Set trigger for your object-deciding-form-field. More can be done if you post something more, or upload some source code at dropbox.com.
//////////
You have set Bindings beforehand, and then you are changing the DataContext. Changing the DataContext will not make your TextBox value to reach the NickName property.
Secondly, you have set UpdateSourceTrigger to PropertyChanged. So, after changing your DataContext you have to make this property appear changed.
One way to do this is to :
DataContext = NewDog;
/* this will trigger property changed */
txtDogNickName.Text = txtDogNickName.Text;
string newNickName = NewDog.NickName; // updated value appear here
But this is not the proper way to do. Change your DataContext sepearately, and then edit values in your form. Changes will propagate correctly. Don't do everything in same handler. For example, you might show this form in some Button1's click handler and set DataContext, then use another Button2's click handler to get form values.
My problem turned out to be the same issue as this post. I am using OneWayToSource and when I change the DataContext my property is set to a default value which is currently "" instead of the value of the target as I was expecting. I have found no work around. I will design such that I know what object to create in advance so that the DataContext will be set in the constructor.
I have the TreeView with objects. When I select item of these tree, than other control - ListView displays as its items properties of selected object. I want to save values of properties when in TreeView selection is change to other object.
So, is there a good way in WPF to gets values of "just before changing" items in ListView control? My idea for now is to override the PreviewMouseDown to check if user click tree node. By god way I mean better than mine. Maybe something in ListView template?
Indication that there is no need to change my idea with the PreviewMouseDown will be also good answer.
Could you please provide the relevant code snippets? I try to answer your question, but I'm not sure I understood it correctly.
If you bind the SelectedItem of you TreeView to a property (a.e. using MVVM pattern), you can save the values before actually setting the item.
Doing so in the setter is not so good though, because it becomes quite large then. I would have a setter like this:
private Foo bar;
public Foo Bar
{
get { return bar; }
set
{
OnPropertyChanging("Bar");
bar=value;
OnPropertyChanged("Bar");
}
}
Then you can listen to your own PropertyChanging events and do your stuff there:
private void this_PropertyChanging(object param, PropertyChangingEventArgs e)
{
switch(e.PropertyName)
{
case "Bar":
//Do you stuff
break,
}
}
I am having trouble with the MVVM pattern and Commands in my WPF app. The problem is not so much the MVVM pattern, but more the stuff that is going on on my GUI. I'll explain the situation:
My app can DoStuff to some files. I have a class with a function DoStuff(int limit). My user user interface has the following items:
A Button DoStuffBtn to start parsing.
A TextBox LimitTxt to fill in a limit.
A CheckBox LimitChk to enabled or disable the limit.
When you would "uncheck" LimitChk, then LimitTxt.Text = "" and LimitTxt.IsEnabled = false. When you would "check" LimitChk, then LimitTxt.IsEnabled = false again, but the text remains empty until you fill something in.
I have read many tutorials on Commands in WPF and MVVM but I just can't seem to pour my case into that mold. The example I gave is actually just a small part of my UI, but I can't seem to do this nicely either.
I keep running into questions like:
Do I need two Commands for LimitChk (enable and disable) or just one (toggle)?
If I bind an int to LimitTxt, what happens if I make it empty and disable it?
Is it a clean way to just use DoStuff(Int32.Parse(LimitTxt.Text)) when DoStuffBtn is pressed?
If I use two commands on LimitChk, what happens with the CanExecute() function of ICommand that determines whether LimitChk is enabled?
So the main question is: How would the situation I described fit into a nice pattern using Commands in WPF?
Some links on WPF, Commands and MVVM i've looked at:
http://www.devx.com/DevX/Article/37893/0/page/1
http://msdn.microsoft.com/en-us/magazine/cc785480.aspx?pr=blog
http://jmorrill.hjtcentral.com/Home/tabid/428/EntryId/432/MVVM-for-Tarded-Folks-Like-Me-or-MVVM-and-What-it-Means-to-Me.aspx
http://msdn.microsoft.com/en-us/magazine/dd419663.aspx
What I understand so far is that I have to keep as much as possible out of the UI. Even stuff like UI influencing the UI. I.e. unchecking LimitChk disables LimitText. Still, I think I should keep a difference between UI related information and actions and stuff that actually has to do with the actual work that has to be done.
I think you're getting confused... you don't need any commands here, you can just use bindings.
Do I need two Commands for LimitChk (enable and disable) or just one (toggle)?
You need none. Just create a LimitEnabled property in your ViewModel, and bind the CheckBox to it (IsChecked="{Binding LimitEnabled}")
If I bind an int to LimitTxt, what happens if I make it empty and disable it?
Disabling it has no effect. If you make the TextBox empty, the binding will fail because an empty string can't be converted to an int (at least not with the default converter)
Is it a clean way to just use Parse(Int32.Parse(LimitTxt.Text)) when ParseBtn is pressed?
You don't need to. Just create a Limit property in your ViewModel, and bind the TextBox to it. You might want to add an ExceptionValidationRule to the Binding so that it highlights invalid input.
The button is not necessary, the parsing will be done automatically when the TextBox loses focus (if you use the default UpdateSourceTrigger). If you want to customize the way it's parsed, you can create a custom converter to use in the binding.
Just some high level thoughts, leaving out superfluous stuff like Color and alignment attributes, WrapPanels, etc.
Your ViewModel has a a couple properties:
public bool? LimitIsChecked { get; set; }
public bool LimitTextIsEnabled { get; set; } //to be expanded, below
public ICommand ParseCommand { get; private set; } // to be expanded, below
public string LimitValue { get; set; } // further explanation, below
Your XAML has CheckBox and TextBox definitions something like:
<CheckBox Content="Limit Enabled" IsChecked="{Binding LimitIsChecked}" />
<TextBox Text="{Binding LimitValue}" IsEnabled="{Binding LimitIsEnabled}" />
<Button Content="Parse" Command="{Binding ParseCommand}" />
You'll want to initialize ParseCommand something like this:
this.ParseCommand = new DelegateCommand<object>(parseFile);
Now, let's fill in that LimitTextIsEnabled property too:
public bool LimitTextIsEnabled {
// Explicit comparison because CheckBox.IsChecked is nullable.
get { return this.LimitIsChecked == true; }
private set { }
}
Your parseFile method would then pass the value of the LimitValue property to the logic doing the actual parsing.
I declared the LimitValue property as string here to avoid cluttering up the code with an explicit converter, or other validation code. You could choose to handle that "LimitValue is a valid int" verification/conversion in several different ways.
Of course, I haven't implemented this in its entirety, but I wanted to outline a pattern where you are not using Commands to update the state of the other widgets. Instead, bind those attributes to properties that are managed in your ViewModel.