wpf collectionviewsource in viewmodel or xaml code-behind - c#

I am in this dilemma and hope someone can help me out
sorry I cannot paste code here as company block posting here.
i am trying to use collectionviewsource in xaml. i tried two ways, static resource and cvs.source. first one works pretty well but limitation is i can only find resource from code-behind. but control ui and disaplay ui not on same view, i don't know how to trigger sort/filter
so i move to second option, i put cvs in view model with properties exposed to both ui. but i got this famous error "trying to change ui not owned by this thread"
so generally what is good practice of where to put csv. i checked many places suggesting second option http://www.xamlplayground.org/post/2009/07/18/Use-CollectionViewSource-effectively-in-MVVM-applications.aspx and XAML Binding to a CollectionViewSource property on a ViewModel but seems no one mentioned ui thread ownership issue. am I doing something really stupid
thanks

If you keep having problems with threads, use a Dispatcher:
Application.Current.Dispatcher.Invoke(
new Action(() => /* modify the collection */));
Or use EnableCollectionSynchronization method, which is new in WPF 4.5 and will do the same for you:
private static object syncObject = new object();
//...
BindingOperations.EnableCollectionSynchronization(yourCollection, syncObject);
Read more about it here.

Related

Calling Usercontrol method from another form

I have this Usercontrol with a Listview loaded in the Mainwindow:
<Controls:MetroAnimatedSingleRowTabControl Grid.Row="1" x:Name="MainTabControl" Controls:TabControlHelper.IsUnderlined="True" Margin="10,0,0,1">
<TabItem Controls:ControlsHelper.HeaderFontSize="40" Header=" List" Foreground="#CCB5BABB" Controls:ControlsHelper.HeaderFontStretch="UltraExpanded" HorizontalAlignment="Left" VerticalAlignment="Top" >
<load:Usercontrol1 DataContext="{Binding}" />
</TabItem>
From this Usercontrol a ButtonClick calls another form for entering new data. After saving the data to database, I call a method loading the list in Usercontrol by referencing the entire Usercontrol to the entry window :
private readonly Usercontrol1 temp;
public newDataEntry(Usercontrol1 temp2)
{
InitializeComponent();
temp= temp2;
}
private void buttonentry(object sender, RoutedEventArgs e)
{
temp.fillList(); // list in Usercontrol fill
this.Close();
}
Since I want to use the same entry form with different Usercontrols, is there a more effective way to call method in Usercontrol?
Without a good Minimal, Complete, and Verifiable example that shows clearly what you are doing, why you want to call this method, what the method does, and what specific problem you are having generalizing the action, it is impossible to know for sure what the best answer for your scenario is. That said, some discussion can be provided.
First and foremost, it is a mistake for your newDataEntry class to depend on the Usercontrol1 class at all. This should already be apparent, due to the issue you are running into trying to reuse it with other UserControl classes, but it is also a basic OOP concept: a class that exists to support some other class should not itself carry a dependency on that other class. Doing so breaks reusability in a way that is fundamentally opposite a primary goal of OOP.
So how do you get rid of this dependency? Well, the most general way in C# would be for your Usercontrol1 to subscribe to the newDataEntry object's Closed event. Then it can do whatever it wants at that time, including calling its own fillList() event.
Of course, if the newDataEnty window is used modally (i.e. you call ShowDialog()), then subscribing to the Closed event is overkill. You can just call whatever code you need to when the ShowDialog() method returns.
All that said, the name fillList() hints that you're copying list data directly into some list-based control (e.g. the ListView you mentioned). When in fact, in a WPF program, you should be manipulating only view models and letting the UI respond accordingly. Again, without a good MCVE showing context, it's impossible to say for sure that's what you're doing, never mind provide any specific advice along those lines. Suffice to say, it's likely that this code doesn't belong in the Usercontrol1 class at all.
See also these related posts:
How to call method of the main WPF window from the modal window? - this seems most applicable. Unfortunately, the accepted and top-voted answer is one of the worst (introduces exactly the kind of class coupling you're trying to avoid here), but there are other answers with some useful information.
WPF MVVM call ViewModel Save method on Window Close - this discusses doing something similar in the context of using a proper view model. May or may not be directly applicable to your scenario.
Communicate between two windows forms in C# - this is about Winforms, but in this particular scenario the basic techniques are similar. In this particular case, you wouldn't need to declare a new event, because the Closed event already seems to do what you want.

Caliburn.Micro - ShowDialog() how to close the dialog?

EDIT:
New Information, just managed to get a logger working (I honestly had no idea cm had one!) and i'm given this message when attempting to use TryClose().
TryClose requires a parent IConductor or a view with a Close method or IsOpen property
I have been stuck on this for a number of days now, and research has turned up zero, I tried posting a question previously about this issue but it received no answers so i assume I didn't word it correctly.
I have a view and viewmodel ContentView/Model which has the following code in them:
ContentView:
<MenuItem Header="New Project" x:Name="OpenProject" cal:Message.Attach="[Event Click] = [Action NewProject()]"/>
ContentViewModel:
public void NewProject()
{
NewProjectViewModel viewModel = new NewProjectViewModel(_projectManager);
_windowManager.ShowWindow(viewModel);
//If the result is true, we have a new project, otherwise they cancelled the window.
if (viewModel.Result)
{
Project newP = new Project(0, viewModel.ProjectNo, viewModel.ProjectName, 0, 0);
_projectManager.Insert(newP);
}
}
and the viewmodel NewProjectViewModel has the following:
public void Create()
{
this.Result = true;
TryClose(true);
}
which is called in the same was as previously using an message.attach on the OK button of the dialog.
However the issue is that TryClose() always fails to close the dialog, and as i don't have the source of caliburn.micro i can't debug inside TryClose() however doing (GetView() As Window).Close() also fails because GetView() always returns null.
I'm at a complete loss as to how i can close this dialog, so any help or suggestions would be greatly appreciated.
EDIT:
Since i seem to be getting no answers on this, where as previous questions do, I'll assume i have information missing. In an attempt to understand the issue i think it may have something to with using the view first approach.
In the NewProjectView i have the following:
xmlns:cal="http://www.caliburnproject.org"
cal:Bind.Model="ShippingClient.ViewModels.NewProjectViewModel"
This is used to bind the viewmodel rather than the automatic way that is usually used, perhaps this is why GetView() returns null?
You are going to absolutely kick yourself:
Remove the cal:Bind.Model and cal:View.Model bindings...
If you are working ViewModel-First (i.e. you are creating a viewmodel and showing it using WindowManager or in a conductor) all the binding stuff that glues the viewmodel to the view is done for you by CM.
In this case you shouldn't use any View-First bindings. What you are essentially doing is newing up another instance of your VM and binding that to your view... so you have two viewmodels in the background, one wired up nicely but not bound any more, and a non-wired up instance which is bound to your view but doesn't know about the conductor.
Just remove any bindings to the viewmodel in your view and it will all work!

Refresh View databindings using UpdateLayout or some other alternative

I have an interface IScreenViewModel, and to simplify the problem I can
RedScreenViewModel : IScreenViewModel
GreenScreenViewModel : IScreenViewModel
etc..
This means I have a RedScreenView.xaml which makes a RedScreenViewModel instance, likewise for all subsequent color screens.
IScreenViewModel has some properties that you must implement e.g.
interface IScreenViewModel
{
public Color ScreenColor{get;set;}
}
I have a ViewmodelWrapper class which holds all viewmodels instances. ScreenViewModels, MenuViewModels etc...
Because I am using DevExpress I can't bind the DataContext directly in the Main.xaml.cs file for reasons I still don't know yet.
So in main for example.
I can't have
ScreenLabel.DataContext = viewModelWrapper.ScreenViewModel
I would have to do in main:
DataContext = viewModelWrapper;
This way the parent Window can see all child elements.
In a RedScreenView.xaml I can have something like this:
<Label Background="ScreenViewModel.ScreenColor"/>
And hopefully the data binding should look in the ViewModelWrapper find the IScreenViewModel.ScreenViewModel object and use the correct ScreenColor object using dynamic binding/polymorphism.
There are cases where a screen can have more properties, so let say in
GreenScreenViewModel along with ScreenColor property inherited, it can have its own property maybe DifferentProperty.
The problem is: I have a Factory that returns a screen object depending on what screen the user wants. It returns the correct screen object, but when it notifies the View to update itself it looks at the new object but using the wrong XAML. If that makes any sense.
I do something like this in a ViewModelWrapper method.
MainGui.ScreenWrapper.LayoutRoot.Clear() ;
MainGui.ScreenWrapper.Items.Clear() ;
MainGui.ScreenWrapper.LayoutRoot.Add(screenFactory.GetSelectedScreen("RedScreen").GetLayoutRoot()
MainGui.UpdateLayout() ;
ScreenViewModel = screenFactory.GetSelectedScreen("RedScreen").GetViewModel() ;
Ignore the fact that I called factory twice...
ScreenWrapper is LayoutGroup that holds the screens. When I swap the Views (screens) using that code I am hoping that it would use the correct bindings.
So let's say I swap from GreenScreenViewModel to RedScreenViewModel, remember GreenScreenViewModel one more property than RedScreenViewModel and in the GreenScreenView I had something like this:
<Label Content="ScreenViewModel.DifferentProperty"/>
When the swap is done and ScreenViewModel notifies that is now pointing to RedScreenViewModel it throws an exception. I am strongly assuming this is because Layout isn't being refreshed and it is still using the wrong view.
The output error in debug mode is
"Cannot find property DifferentProperty in viewModelWrapper.ScreenModel"
Which isn't right because I have already deleted THAT GreenScreenView, I updated the layout, I know there is a LayoutChanged event or something like that, so that could have been raised as well so why is it still seeing the wrong View?
How can I update ScreenWrapper.LayoutRoot to "see" the new View with a different binding code.
Heavens, I hope that was clear.
EDIT: At Michael thanks for replying. Yes there is an actual exception - "NullReferenceException" in the thirdparty dll I am using. And that is because it can't find the property. I am sure I didn't make myself clear but maybe the question should be: When deleting and inserting usercontrols from a visual tree -how can I refresh the visual tree to see new bindings? If I can refresh the visual tree it should solve my problem. UpdateLayout() doesn't work
EDIT:
At Michael thanks for replying. Yes there is an actual exception - "NullReferenceException" in the thirdparty dll I am using. And that is because it can't find the property. It throws the exception when I call OnPropertyChanged, and yeah the handler isn't null!
I am sure I didn't make myself clear but maybe the question should be:
When deleting and inserting usercontrols from a visual tree -how can I refresh the visual tree to see new bindings? If I can refresh the visual tree it should solve my problem.
UpdateLayout() doesn't work.
Firstly, you say
Because I am using DevExpress I can't bind the DataContext directly in the Main.xaml.cs file for reasons I still don't know yet.
Express should not be the problem here. You need something to bind to that will return the appropriate ViewModel. Have a look here for discussion on this subject.
Secondly, you say the error is
Cannot find property DifferentProperty in viewModelWrapper.ScreenModel
This is not necessarily a problem, and does not cause an exception. When you change bindings dynamically, INotifyPropertyChanged events fly off all over the place and there may be a period of 'uncertainty'. I am assuming your ViewModels implement INotifyPropertyChanged.
I think the key is probably looking closer at the exception, if there is one (because "Cannot find property" is a debug message, not an exception). To get some clarity you might want to turn off the binding message as described here. If there is an actual exception, please edit with the details.

Access Pivot Control from App.xaml.cs

In my MainPage.xaml, I created a Pivot Control: <controls:Pivot Title="Powder God" Name="PivotControl">.
My first pivot view is a HubTile that summarize all other individual pages. So my application bar will be different between the first pivot view and all other ones.
That's why I put my application bar in App.xaml's resource section, then load based on selected index of my pivot.
My question is:
In the application bar I will be using for all individual pages, I want to have a delete option, where I will remove that specific item (a view model) from my data context.
I know I can use PhoneApplicationFrame root = Application.Current.RootVisual as PhoneApplicationFrame; to access navigation services, but I don't know how can I reference to my pivot, so that I can get the selected index and proceed forward.
Thanks!
Using MVVM you SHOULDN'T do this:
((PageType)Application.Current.RootVisual).PivotControl. //Blah
PageType is whatever type PhoneApplicationFrame is that contains your PivotControl. If this doesn't work you need a Property in the RootVisual
PAGE
public Pivot MyPivot
{
get
{
return PivotControl;
}
}
APP
((PageType)RootVisual).MyPivot. //Blah
On one level Microsoft's suggestion of putting the ApplicationBar in App.xaml is great as it can be referenced from everywhere and would appear to encourage code reuse: however this question highlights the limit to this approach. An application bar is typically used to provide actions which are specific to the current page (or pivot item) and just because the buttons are the same you may not want the exact same code to run in each case.
In this case I think it would better to create a factory method that creates your common ApplicationBar with the click handlers you specify specific to your page/pivot item. For bonus points put the method in a new class (not App) so it doesn't get lost in all the boilerplate code there. Call this factory method in your page constructor and remember your ApplicationBar in your class. For multiple app bars, create them all up front and you can then easily switch between these app bars in your Pivot SelectionChanged code.
The alternative of creating the ApplicationBar in App.xaml and then retrieving this from the App.xaml.cs "Resources" ResourceDictionary in code, modifying the click callbacks, is more complicated in my opinion.
I wish they'd done a better job of implementing the ApplicationBar so people wouldn't want to do this. I've found that using the ApplicationBar forces you to add code to your Page.xaml.cs even if you use a framework like MVVM Light. This is still OK in MVVM as it's UI specific code that belongs in the View, but it makes things inconsistent if you're using ICommand everywhere else. Last time I decided it was better to create the entire ApplicationBar in code rather than hack this kind of thing via App.xaml.cs.
Update: There is a UserVoice request for a data bindable ApplicationBar.

How not to lose binding source updates?

Suppose I have a modal dialog with a textbox and OK/Cancel buttons. And it is built on MVVM - i.e. it has a ViewModel object with a string property that the textbox is bound to.
Say, I enter some text in the textbox and then grab my mouse and click "OK". Everything works fine: at the moment of click, the textbox loses focus, which causes the binding engine to update the ViewModel's property. I get my data, everybody's happy.
Now suppose I don't use my mouse. Instead, I just hit Enter on the keyboard. This also causes the "OK" button to "click", since it is marked as IsDefault="True". But guess what? The textbox doesn not lose focus in this case, and therefore, the binding engine remains innocently ignorant, and I don't get my data. Dang!
Another variation of the same scenario: suppose I have a data entry form right in the main window, enter some data into it, and then hit Ctrl+S for "Save". Guess what? My latest entry doesn't get saved!
This may be somewhat remedied by using UpdateSourceTrigger=PropertyChanged, but that is not always possible.
One obvious case would be the use of StringFormat with binding - the text keeps jumping back into "formatted" state as I'm trying to enter it.
And another case, which I have encountered myself, is when I have some time-consuming processing in the viewmodel's property setter, and I only want to perform it when the user is "done" entering text.
This seems like an eternal problem: I remember trying to solve it systematically from ages ago, ever since I've started working with interactive interfaces, but I've never quite succeeded. In the past, I always ended up using some sort of hacks - like, say, adding an "EnsureDataSaved" method to every "presenter" (as in "MVP") and calling it at "critical" points, or something like that...
But with all the cool technologies, as well as empty hype, of WPF, I expected they'd come up with some good solution.
At critical points, you can force the binding to push through to your view model:
var textBox = Keyboard.FocusedElement as TextBox;
BindingOperations.GetBindingExpression(textBox, TextBox.TextProperty).UpdateSource();
Edit:
OK, since you don't want hacks we have to face the ugly truth:
In order to implement a clean view, the properties exposed by your view model should be friendly to frequent binding updates.
An analogy we can use is a text editor. If the application was a giant text box bound to a file on disk, every keystroke would result in writing the whole file. Even the concept of saving is not needed. That's perversely correct but terribly inefficient. We all immediately see that the view model needs to expose a buffer for the view to bind to and this re-introduces the concept of save and forces state handling in our view model.
Yet, we see this is still not efficient enough. For even medium-sized files the overhead of updating the whole-file buffer on every keystroke becomes unbearable. Next we expose commands in our view model to efficiently manipulate the buffer, never actually exchanging the whole buffer with the view.
So we conclude that in order to achieve efficiency and responsiveness with pure MVVM, we need to expose an efficient view model. That means that all text boxes can be bound through to properties with no ill effects. But, it also means that you have to push state down into the view model to handle that. And that's OK because a view model is not the model; it's job is it to handle the needs of the view.
It's true that we can rapidly prototype user interfaces by taking advantage of shortcuts like binding on focus changes. But binding on focus changes can have negative consequences in real applications and if so then we should simply not use it.
What is the alternative? Expose a property friendly to frequent updates. Call it the same thing as the old inefficient property was called. Implement your fast property using the slow property with logic that depends on the state of the view model. The view model gets the save command. It knows whether the fast property has been pushed through to the slow property. It can decide if when and where the slow property will be synched to the model.
But you say, haven't we just moved the hack from the view to the view model? No, we have lost some elegance and simplicity, but go back to the text editor analogy. We have to solve the problem, and it is the view model's job to solve it.
If we want to use pure MVVM and we want efficiency and responsiveness, then lame heuristics like let's avoid updating the binding source until the element loses focus won't help. They introduce as many problems as they solve. In that case, we should let the view model do its job, even if means adding complexity.
Assuming we accept it, how can we manage the complexity? We can implement a generic wrapper utility class to buffer the slow property and allow the view model to hook its get and set methods. Our utility class can auto-register for save command events to reduce the amount of boilerplate code in our view model.
If we do it right, then all the parts of the view model that were fast enough to be used with property changed binding will all still be the same, and the others that were worthy of asking the question "Is this property too slow?" will have a small amount of code to address the issue, and the view is none the wiser.
This is a tricky one and I agree a non-hack and more-or-less code free solution should be found. Here are my thoughts:
The view is responsible because it sets IsDefault to true and allows for this 'problem'
The ViewModel should not be responsible in any way to fix this it might introduce dependencies from VM to V and thus breaking the pattern.
Without adding (C#) code to the View all you can do is either change bindings (e.g. to UpdateSourceTrigger=PropertyChanged) or add code to a base class of the Button. In the base class of the button you might be able to shift focus to the button before executing the command. Still hack-ish but cleaner than adding code to the VM.
So at the moment the only 'nice' solutions I see require the view developers to stick to a rule; set the binding in a specific way or use a special button.
I would add a Click event handler for the default button. The button's event handler is executed prior the command will be called, so the data bindings can be updated by changing the focus in the event handler.
private void Button_Click(object sender, RoutedEventArgs e) {
((Control)sender).Focus();
}
However, I don't know if similar approach can be used with other shorcut keys.
Yes, I have quite some experience. WPF and Silverlight still have their pain areas. MVVM doesn't solve it all; it is not a magic bullet and the support in the frameworks is getting better but still lacks. E.g., I still find editing deep child collections a problem.
At the moment I handle these situations case by case because a lot depends on the way the individual view have to work. This is what I spend most of my time on because I generate a lot of plumbing using T4 so I have time left for these quirks.
The problem is that the TextBox's text has a default source trigger of LostFocus instead of PropertyChanged. IMHO this was a wrong default choice since it is quite unexpected and can cause all sorts of problems (such as the ones you describe).
The simplest solution would be to always explicitly use UpdateSourceTrigger=PropertyChanged (as the others suggested).
If this isn't feasible (for whatever reason), I would handle the Unloaded, Closing or Closed events and manually update the binding (as shown by Rick).
Unfortunately it seems that certain scenarios are still a bit problematic with a TextBox, so some workarounds are necessary. For example, see my question. You might want to open a Connect bug (or two) with your specific problems.
EDIT:
Pressing Ctrl+S with focus on the TextBox, I would say the behavior is correct. After all, you are executing a command. This has nothing to do with the current (keyboard) focus. The command may even depend on the focused element! You are not clicking on a button or similar, which would cause the focus to change (however, depending on the button, it may fire the same command as before).
So if you want to only update the bound Text when you lose focus from the TextBox, but at the same time you want to fire a command with the newest contents of TextBox (i.e. the changes without it having lost focus), this does not match up. So either you have to change your binding to PropertyChanged, or manually update the binding.
EDIT #2:
As for your two cases why you cannot always use PropertyChanged:
What precisely are you doing with StringFormat? In all my UI work so far I use StringFormat to reformat data I am getting from the ViewModel. However, I am not sure how using StringFormat with data which is then again edited by the user should work. I am guessing you want to format the text for display, and then "unformat" the text the user enters for further processing in your ViewModel. From your description, it seems it isn't "unformatted" correctly all the time.
Open a Connect bug where it isn't working as it should.
Write your own ValueConverter which you use in the binding.
Have a separate property with the last "valid" value and use that value in your ViewModel; only update it once you get another "valid" value from the property you use in databinding.
If you have a long-running property setter (ex. a "validate" step), I would do the long-running part in a separate method (getters and setters should normally be relatively "fast"). Then run that method in a worker thread/threadpool/BackgroundWorker (make it abortable so you can restart it with a new value once the user enters more data) or similar.
What you think about proxy command and KeyBinding to ENTER key?
EDITED:
There we have one utility command (like converter), which requires knowledge about concrete view. This command can be reused for any dialog with same bug. And you add this functionality/hack only in view where this bug exists, and VM will be clear.
VM creates to adapt business to view and must provide some specific functionality like data conversion, UI commands, additional/helper fields, notifications and hacks/workarounds. And if we have leaks between levels in MVVM we have problems with: high connectivity, code reuse, unit testing for VM, pain code.
Usage in xaml (no IsDefault on Button):
<Window.Resources>
<model:ButtonProxyCommand x:Key="proxyCommand"/>
</Window.Resources>
<Window.InputBindings>
<KeyBinding Key="Enter"
Command="{Binding Source={StaticResource proxyCommand}, Path=Instance}"
CommandParameter="{Binding ElementName=_okBtn}"/>
</Window.InputBindings>
<StackPanel>
<TextBox>
<TextBox.Text>
<Binding Path="Text"></Binding>
</TextBox.Text>
</TextBox>
<Button Name="_okBtn" Command="{Binding Command}">Ok</Button>
</StackPanel>
There used special proxy command, which receive element (CommandParameter) to move focus and execute. But this class requires ButtonBase for CommandParameter:
public class ButtonProxyCommand : ICommand
{
public bool CanExecute(object parameter)
{
var btn = parameter as ButtonBase;
if (btn == null || btn.Command == null)
return false;
return btn.Command.CanExecute(btn.CommandParameter);
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
if (parameter == null)
return;
var btn = parameter as ButtonBase;
if (btn == null || btn.Command == null)
return;
Action a = () => btn.Focus();
var op = Dispatcher.CurrentDispatcher.BeginInvoke(a);
op.Wait();
btn.Command.Execute(btn.CommandParameter);
}
private static ButtonProxyCommand _instance = null;
public static ButtonProxyCommand Instance
{
get
{
if (_instance == null)
_instance = new ButtonProxyCommand();
return _instance;
}
}
}
This is just idea, not complete solution.

Categories

Resources