I have a screen with a TextBox in which the user can type a 2-character state code. Below the TextBox is a ListBox containing all 50 state codes. The TextBox is bound to property in the VM, and the SelectedItem is bound to a property in the VM. That all works fine.
The way I want the UI to work is when the user selects a state from the ListBox, the TextBox is automatically filled in, and this works fine.
Where it gets messy is when the user types in the state in the TextBox. When I get the first character, what I want to do is reposition the list box at the first matching state code for that letter, so for instance, if the ListBox is sitting at "AK" (Alaska) and the user is going to type "ID" for Idaho, when I get the "I" I want to position the ListBox so you can see the first "I" state, which is "IA" (Iowa).
If I use code behind and point SelectionChanged=BringSelectionIntoView with this method coded as follows, it works great:
private void BringSelectionIntoView(object sender, SelectionChangedEventArgs e)
{
ListBox lb = (ListBox)sender;
lb.ScrollIntoView(lb.SelectedItem);
}
All I have to do is scan the list of state codes until the first letter matches, then update the Index property to which SelectedIndex is bound, and poof, the BringSelectionIntoView() method gets invoked and I have exactly the UI behavior I want.
Trying to do this in a purest MVVM methodology, however, has proved quite frustrating. I'm not using MVVMLight or ExpressionBlend--I want a simple way to do this in MVVM. I understand the purest's mindset of not putting any UI code in the view, but the framework is insanely cumbersome to enact this kind of behavior. There's a point of diminishing returns when you have to create such obtuse plumbing to force yourself to adhere to a pattern when it's far more practical to put in the method with 2 lines of code that works perfectly.
So my question is this: am I doing something wrong and is there a simple way to make this work without violating MVVM? It's disappointing if the solution requires additional SDKs or someone's framework. That would suggest that MVVM doesn't have particularly good legs to stand on in a generic OOP sense.
Does someone see an error in what I'm trying to do, or do you see a simplistic solution here? Thanks!
MVVM is not about not having any code behind.
What you're talking about here is VIEW behavior. Which fits perfectly in the code behind, as long as you're not messing with the DATA in the event handlers.
You're using a VIEW event handler to manipulate a VIEW aspect.
That doesn't break MVVM.
Keep it that way. Keep it Simple.
You should still have a ViewModel and a Model to hold the DATA that the UI shows.
This is a perfect use case for an attached behavior. You can write this behavior once and use it for all listboxes without ever having to write any additional code. If you would like me to elaborate, ask and I'll post more information.
Related
Mvvm, Wpf, VS2015
Hi All! I have googled this till my fingers bleed! But just hacks and work-arounds, or partial use of code behind.
My Wpf-Mvvm app uses Speech Synthesis.
Each button, (and in the next iteration, buttons using the selectedItem Index of Lists or ComboBoxes, to select the content(text)to be synthesized) specifies different content to be synthesized.
Sticking with Mvvm principles, in my viewModel, I need to capture which button, based on the List or ComboBoxes' SelectedItem/Value that is bound to the Parameter Command.
Why? So I can have the app synthesize the content(text/words)the user selects to hear. If not, I have to make separate commands and speech synthesizer methods for each.
There must be a simple clear-cut way. What am I missing here?
I thought of updating the Model's properties to reflect user's selection and use User Settings to persist,but have not tried yet.
Otherwise the Model's properties are accessible so far as encapsulation-wise to the ViewModel, but the property values from the Model still are not visible in viewModel.
I need the command bound control's ID/Name to route the content to be synthesized based on the model's class properties. But when I instantiate the model from the viewModel,
the model's properties are not there. I presume instantiating the Model class object from the viewModel, is in a different memory location, than the Model.
So I will try using User Setting's persistence for these properties, if no one else has a better way.
There has to be a clear solution to this, I hope :)
I know the View and ViewModel are supposed to not need to know what the other is doing. In this case though, I clearly need the List or ComboBoxes' Id or Name and their SelectedItem index.
I also need the button's Id or Name, because I have several groupings of content to choose from, and several buttons starting the synthesizing method.
Example: If the text content of an announcement to be synthesized, held in separate XML(SSML) files with identifier key values of 941, 23, 508, etc.,
I need to know which the User Selected to hear.
I imagine simply capturing in the viewModel, the Data-bound List/ComboBoxes' Selected ID index would suffice.
Thank you so very much for your help.
-Flazz
Is there a way to have the selected items of a ListBox as a ListBoxItem in the event SelectionChanged (on the ListBox) in WPF?
Right now, when I call listBox.SelectedItems I get the list of items in the format of my data source.
For example, when I create my ListBox I bind it to a CustomListBoxViewModel as a data source, so when I called SelectedItems I get a list of CustomListBoxViewModel objects.
Thanks!
EDIT:
The code that was given in the chosen answer worked perfectly for my use case.
On the other hand, I completely understand that this is a violation of the MVVM pattern. I'll use the code you provided in code behind of a xaml view.
The reason behind my original question was that I need to implement a ListBox that has some disabled ListBoxItems inside. Those items would have to be unselectable.
I tried to use an Attached Property IsSelectable on my ListBoxItems and it didn't work well. This attached property was greatly inspired by this post. The problem was that when I was directly selecting a disabled item, the attached property worked perfectly. The item couldn't be selected and it wasn't inside the SelectedItems collection. But if I wanted to select all the items by pressing CTRL+A, all the ListBoxItems were selected even the disabled ones and were found in the SelectedItems collection.
Whatever reason you're doing this for, it's probably a serious violation of MVVM that you'll bitterly regret for the rest of your life.
But the first step on the road to perdition is always an easy one.
Here's how:
var listBox = (ListBox)sender;
var selectedListBoxItems =
listBox.SelectedItems.Cast<Object>()
.Select(item => (ListBoxItem)listBox.ItemContainerGenerator.ContainerFromItem(item))
.ToList();
Just don't come crying to me when it all ends in tears.
No but seriously, there's are a few good reasons to do this kind of thing in WPF, but they're far less common than somebody new to WPF would expect. Pretty much any normal case is best handled by having your C# code interacting only with your data items, and do stuff to the ListBoxItems in XAML via styles and templating. Once you get used to that way of thinking, it's very powerful, flexible, productive, and maintainable. Codebehind is what you do for odd cases when all other reasonable avenues fail. Drag and drop, for example.
I'm currently in the need of setting the SelectedIndex property of my TabControl when a certain event (IEventAggregator) takes place and thought about how I'd implement that.
I came up with 2 possibilities:
Use GetView() provided by ViewAware in order to access my TabControl and set the SelectedIndex to my value
Use a property in my associated ViewModel and bind this property to my TabControl's SelectedIndex property via XAML
Both options are working fine but I personally want to get this question answered since this is not the first time I'm wondering where to implement the functionality in such cases.
I know that the first option won't enable the Notify support but besides that: What would be the proper way?
Having a GetView() method to manipulate the view directly from the viewmodel completely breaks MVVM. You might as well just put all your logic in codebehind. The whole point of MVVM is to abstract away the actual view so that it is decoupled from the logic, and the app can be unit tested.
What if you change your mind about the tabs in the future and decide to show your multiple views some other way? You've now got to start editing your viewmodel to edit the new view instead of just tweaking some XAML.
And for unit testing you're going to have no way to mock out your TabControl.
I am required to use the mvvm pattern. I know that the viewmodel should not care about the view from what I been reading. As a result I don't know how to solve this problem:
I have a dll that basically turns a textbox and listview into an autocomplete control:
SomeDll.InitAutocomplete<string>(TextBox1, ListView1, SomeObservableCollection);
anyways I don't know how to call that method from the viewmodel using the mvvm patter. if I reference the controls in the view I will be braking the rules.
I am new to MVVM pattern and my company requires me to follow it. what will be the most appropriate way of solving this problem?
I know I will be able to solve it by passing the entire view to the viewmodel as a constructor parameter but that will totaly break the mvvm pattern just because I need to reference two controls in the view.
What you're doing here is a pure view concern, so I'd recommend doing it in the view (i.e. the code-behind). The view knows about the VM and its observable collection, so why not let the code behind make this call?
(I'd also recommend seeing if you can get a non-code/XAML API for "SomeDll", but I have no idea how much control you might have over that)
There are two things that I'd point out here -
First, this is effectively all View-layer code. As such, using code behind isn't necessarily a violation of MVVM - you're not bridging that View->ViewModel layer by including some code in the code behind, if necessary.
That being said, this is often handled more elegantly in one of two ways -
You could wrap this functionality into a new control - effectively an AutoCompleteTextBox control. This would allow you to include the "textbox" and "listview" visual elements into the control template, and bind to the completion items within Xaml.
You could turn this into an attached property (or Blend behavior), which would allow you to "attach" it to a text box, and add that functionality (all within xaml). The items collection would then become a binding on the attached property (or behavior).
I began to make a program in c# using wpf and the mvvm pattern to learn this program language. I feel sorry that I finally have problems I can't solve using Google. But I try being precise.
At first I present you the GUI, so I can explain my problems more easily.
The xaml file of the MainWindow comes here.
As you see, it's not a serious and important program, I just made it to learn some techniques.
Now comes my main question:
I need the SelectedItem Property of the second ListView(Consumables) in the footer. I thought this wouldn't be a serious problem cause I can just bind it to the selected item using relative path and the name of the listview. This didn't work and caused a null reference error as soon as I added an item to this listview. I double checked the code and I am sure I haven't made something wrong there. So I thought the problem is, that it's kind of a descendant ui element.
My next try was to create a new property in my main view model which is bound to the selected item. You can find this in line 136. But as I found out, this causes me a System.NullReferenceException too if i click on an item. I think it's because this property is read only. I don't know what to do. Isn't there any other possibility to bind the SelectedItem as DataContext for the footer?
My Second Question is about line 27. The TabControl should always select the first Tab automatically. But it makes it only every second time. It's quite funny. If I scroll down the Champion Combo Box, the first tab goes: selected - unselected - selected - ...
My third and last Question is about 72 which is similar to line 50 (The DataContext is the same too) But the ICommand of the ContextMenu of the itemtemplate you see line 50 works, whereas the contextmenu (l. 72) does not call the Icommand. Weird.
It's a fun project, so I can provide you the source code if my information are not enough.
--Sorry, only two hyperlinks allowed--
Please help me. I am just a bit confused by this unsuspected behaviour. I haven't found anything that could help me and I'm sorry if the answers of these questions will be simple :P
Just to emphasize it: The main question is the only one I really need an answer for. The second one could be solved programatically. And to solve the third one, I could just remove this feature.
I'm happy for EVERY HINT!
Haven't gone through the entire question but you should bind selected item to your view model property (two way binding) and then bind another UIElement to this view model property (probably one way binding or check for value equality as to not cause stackoverflow exception)