Up until today I've been using MVVM Light's EventToCommand for event handling in XAML. I decided to try out InputBinding for mouse events and so far the results have been far from pleasing. I'm guessing I'm doing something wrong because there's a delay of maybe half a second between mouse clicks. With EventToCommand, the UI will update as fast as I can click it. All this test program does at the moment is fills a circle on a canvas either white or black when it's clicked.
<Canvas>
<Ellipse Canvas.Left="{Binding X}"
Canvas.Top="{Binding Y}"
Width="16"
Height="16"
Fill="Black">
<Ellipse.InputBindings>
<MouseBinding Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type UserControl}},
Path=DataContext.ClickEllipse}"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type Ellipse}}}"
MouseAction="LeftClick" />
</Ellipse.InputBindings>
public RelayCommand<object> ClickEllipse { get; set; }
ClickEllipse = new RelayCommand<object>((o) => ExecuteClickEllipse(o));
private void ExecuteClickEllipse(object o)
{
var obj = o as Ellipse;
if (obj.Fill == Brushes.Black)
{
obj.Fill = Brushes.White;
TestText = "White";
}
else
{
obj.Fill = Brushes.Black;
TestText = "Black";
}
}
What am I doing anything wrong here to cause the delay between clicks? I can't imagine that this would be the intended behavior. I have EventToCommand set up almost identically and it has no problems. Furthermore, assuming I did make a stupid mistake and this isn't intended behavior, are there any advantages of InputBinding over EventToCommand for Key and Mouse events or vice versa? How do the 2 differ in terms of functionality and performance (if that's even an issue in this situation)?
Edit - Something else I've noticed with both InputBinding and EventToCommand (which I have set up almost identically) is that each time the circle is clicked, task manager shows the program's memory usage jumping up a bit. Is this normal?
I've just been looking into your problem... now I don't use that RelayCommand, instead preferring my own ActionCommand, but I've looked at the code for it and they are practically the same. So, I added your code to a new WPF applicaton and here's what I found:
When I click on the circle, it immediately changes colour. If I click on it again, it immediately changes colour again. However, if I double click on the circle, it only changes colour once and I would have expected it to change colour twice. Clicking on it multiple times makes it alternate between white and black, but only at a rate of about two per second... much slower than the rate of clicks.
Changing the Ellipse to a TextBlock and updating the Text property in the click handler had similar results. However, when I replace the MouseBinding with a simple event handler for the PreviewMouseLeftButtonDown event, this 'slow reaction time' disappears.
Continuing my testing, I then added a Button and set the same Command on its Command property (after removing it from the TextBlock). This just used my ActionCommand and it worked immediately and perfectly on every click.
So it appears that there is indeed a delay between multiple invocations of the same function when using this method of routing Commands through a MouseBinding. While this answer may not help you, I hope at least that this confirmation of the problem will help in some small way.
Regarding the memory issue, I wouldn't worry about that. WPF is often quick to ask for memory and slow to let it go.
Related
I have button on the Grid that does not detects PreviewMouseLeftDown click event.
After some testing I figured that the problem is in <Grid PreviewMouseMove="onMouseMove" >
If I remove PreviewMouseMove="onMouseMove" part, then MouseDown event is detected, but i need that line of code, since I also have to detect mouse position inside that grid only.
XAML:
<Grid PreviewMouseMove="onMouseMove" Background="Transparent">
<ItemsControl Name="btnTableImageList">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Content="{Binding Content}"
Height="{Binding Height}"
Width="{Binding Width}"
Tag="{Binding Tag}"
Margin="{Binding Margin}"
Background="{Binding Background}"
HorizontalAlignment="Center"
PreviewMouseLeftButtonDown ="tblButton_MouseDown"
PreviewMouseLeftButtonUp ="tblButton_MouseUp"
Click="ClickHandlerTableBtn"
TextBlock.TextAlignment="Center" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Any idea is welcomed. Thanks!
I totally agree with themightylc, but also understand you... WPF and MVVM are not so "easy" to get used to, I do it for a year more or less, and still have a lot to learn.
In that kind of situation I only could advise you to read some tutorials about WPF, DataBinding and ObservableCollection and ViewModel (these are the keywords you need to know).
1) Create a ViewModel where you can define a ObservableCollection, ObservableCollection is kind of list, but using it you can update your View (almost) automaticaly.so when you launch your application, you will read the list of buttons you need to display, then add them to the ObservableCollection
for your tests will be something like that :
Button button1=new Button();
Button button2=new Button();
//define all dimensions/parameters of your button
MyObservableCollection.Add(button1);
MyObservableCollection.Add(button2);
Then in XAML you just need to specify the ItemsSource of ItemsControls(MyObservableCollection). doing like that you don't need anymore all description of buttons inside.
Then when you click to add a button(in your case), you just need to make in code behind something like
Button newButton=new Button();
newButton.Height=defaultHeight...//width, background etc...
MyObservableCollection.Add(newButton);
again, just for advise if WPF/MVVM is new to you, I would advise to begin with easier samples, make a small listview with simple objects inside, or a listbox.
Could also advise you these websites :
wpf-tutorial.com
www.wpftutorial.net
At the end this is actually working properly.
For test I have set up a label, and in MouseMove event i am sending Mouse Position to that label,lblCoord.Content = Mouse.GetPosition(Application.Current.MainWindow);
In case of MouseClick I am sending lblCoord.Content="MouseClick";
And in case of MouseDown I am sending lblCoord.Content="MouseDown";.
I can see mouse coordinates in lblCoord, I can see MouseClick, but it never displayed MouseDown.
However, if i call MessageBox inside MouseDown event, everything works. So i guess that XAML <Grid> PreviewMouseMove="onMouseMove" works even when I am not moving mouse so it is sending coords to a Label all the time and overwrites lblCoord.Content="MouseDown"; faster than I am able to see it.
The answer to this question is: Don't work with WPF and expect WinForms results...
Thanks to everybody for their time and effort!
I'm having a weird UI issue in my app, where a ScrollViewer jumps to the top as soon as a TextBox child loses focus. I've already tried to set BringIntoViewOnFocusChange="False" in the parent ScrollViewer, but that doesn't solve the issue unfortunately.
Here's a quick video that shows the problem I have:
As you can see, every time the top TextBox is focused, the ScrollViewer jumps back to the top as soon as it loses focus (ie. whenever I tap on an item in the ListViews below. This also happen if I click on one of those ComboBox controls: the ScrollViewer still jumps back immediately.
This is the general structure of the contents of the Popup your're seeing:
<UserControl>
<RelativePanel HorizontalAlignment="Stretch">
<!--Item name-->
<TextBlock Text="Name"/>
<customControls:TextBoxActionButton/>
<customControls:TextValidatorControl/>
<!--Section selector-->
<TextBlock Text="Section"/>
<customControls:ComboBoxWithResetButton/>
<!--Item GroupName-->
<TextBlock Text="Group"/>
<customControls:ComboBoxWithResetButton/>
<!--Template based on-->
<TextBlock Text="Based on"/>
<customControls:ComboBoxWithResetButton/>
<!--Icons list and description-->
<TextBlock Text="Icon"/>
<ListView ScrollViewer.VerticalScrollMode="Disabled"/>
<!--Select color text-->
<TextBlock Text="Select color"/>
<ListView ScrollViewer.VerticalScrollMode="Disabled"/>
</RelativePanel>
</UserControl>
I don't have any code that interacts with the parent ScrollViewer, which is in a completely different UserControl, along with the rest of the Popup UI (header, buttons at the bottom etc..).
I've also tried to subscribe to the LosingFocus event of the first TextBox, and to set e.Handled = true; from there, but that didn't work too.
Do you have any idea on why this is happening? And also, why doesn't the BringIntoViewOnFocusChange="False" property work in this situation?
Thanks!
I think the problem is the TextValidatorControl which causes the TextBox to regain focus, because this does not seem to happen when the control is not there.
Please verify what happens if you actually enter a valid name. If the behavior stops, I would suggest you to invetigate or post the source code of TextValidatorControl, because it looks like the source of the problems.
I have a main window that I use to display one of two possible views (ConfigView & AnalyzeView):
<Window.Resources>
<DataTemplate DataType="{x:Type vm:ConfigViewModel}">
<v:ConfigView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:AnalyzeViewModel}">
<v:AnalyzeView/>
</DataTemplate>
</Window.Resources>
<Grid>
<ContentControl Content="{Binding CurrentViewModel}" />
</Grid>
When transitioning from the ConfigView to the AnalyzeView, I want to first wait for the AnalyzeView to be fully displayed in the main window before performing the analysis operations. I initially added an EventTrigger for the Loaded event in the AnalyzeView as a way of starting the analysis operations:
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding LoadedCmd}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
However, I found that this event would be triggered before the AnalyzeView was displayed in the main window. In fact, it seems that the AnalyzeView is not displayed in the main window until sometime after the Loaded event.
Is there any event that I can trigger on so that I can start the analysis operations only after the AnalyzeView is fully displayed in the main window?
EDIT:
Ultimately, may main goal is to display a progress bar on the AnalyzeView that shows the progress of the analysis operations. Essentially, the user presses a "Start Analysis" button which transitions to the AnalyzeView and begins the analysis process, updating a progress bar as it goes.
I think what you're seeing is that the Loaded event fires after the UI thread has finished loading the control but if you have a breakpoint set in LoadedCmd, you'll notice AnalyzeView isn't rendered yet.
This is because WPF has a background rendering thread which hasn't yet had a chance to render the control, even though the UI thread is finished with it's loading. When your program hits the breakpoint, all threads are broken into and so it appears as though AnalyzeView never finished loading.
I'd suggest trying to allow the command to perform whatever operations are necessary to populate the AnalyzeView just as you've laid out here- if they're long running, and do not have to be loaded into DependencyProperties, you should run them asynchronously so that the UI thread can respond to input.
Edit 1: Following up on your comment's on #wimpSquad's answer - to keep this MVVM and show progress while keeping the UI responsive, you definitely will want to look into Tasks and reporting progress. Stephen Cleary has a good article here.
Further Reading:
WPF Threading Model
Async Loading Of Data
So, looks like you can - SO question - but I'm curious as to why you would want to. The View should only be a rendering of exposed properties in the ViewModel. What is your use case for this (morbid curiosity)?
Edit: based on your updated question, I'd recommend the following:
Load the AnalyzeView as normal, with a <ProgressBar/> bound to an exposed property that is incremented by the ViewModel (during your analysis process). This synchronicity can be achieved through event binding, and made even easier with PropertyChanged.Fody NuGet package, with the [ImplementPropertyChanged] attribute. This attribute was renamed in a more recent version of the package than what I am using, but with a quick search you should find what you are looking for there. In any case, the attribute handles triggering automatically, so as properties are updated in your ViewModel, the bound properties on your View update as well.
If you'd like to see an example of a <ProgressBar/> being used, just let me know. I've got one around here somewhere.
I have a 3 part question about the "Hello Screens" Caliburn Micro example. (I'm using a WPF port of the project). This concerns the behavior of CustomerView.xaml which contains a "Save" and "Close" button pair. Willing to accept answers to just 1 and 2 as 3 probably becomes trivial at this point!
Whenever the AddressViewModel is activated by the DialogConductorViewModel the Save and Close buttons become disabled. This happens here in the EditAddress function of CustomerViewModel:
Dialogs.ShowDialog(new AddressViewModel());
1) How do the user controls of CustomerView know to become disabled here? My first thought was that CustomerViewModel (CMV) must've deactivated and maybe CM disables the the CustomerView UserControl for us as a result. However, if you override OnDeactivate in CMV the view model isn't being deactivated whenever it shows a dialog with the "ShowDialog" function.
2) From a WPF standpoint how do all the controls on CustomerView become disabled? If I put this code somewhere in CustomerView.xaml and run the project, the CustomerView UserControl remains enabled throughout:
<TextBlock Text="{Binding Path=IsEnabled,
RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}}"></TextBlock>
However, if I bind to IsEnabled on any control I place onto CustomerView.xaml that control's IsEnabled property is being set to false when a dialog is shown:
<TextBlock Text="{Binding Path=IsEnabled,
ElementName=TryClose }"></TextBlock>
3) I would like to write a style datatrigger to alter other aspects of CustomerView's appearance whenever it's displaying a dialog. Ideally I would be binding to a property on CustomerViewModel but the best I can tell, this ViewModel doesn't inherently know whenever it's displaying a Dialog through the DialogManager unless there's something I've missed. Does CM give me a way to detect whenever this dialog is being displayed from the point of view of the CustVM?
Thanks,
Sean
I want to use the C# System.Windows.Forms.SendKeys.SendWait() Method to send Keystrokes from an OnScreenKeyboard to a Textbox. Since I may use this OnScreenKeyboard at other places too I created a UserControl with View (for the Design of the Keyboard) and Viewmodel (basically for calling the Sendkeys.SendWait() Method) in an extra project.
Within this project I created a MainView where I included the UserControl via a ContentControl as you can see in the Code below. CurrentPage basically refers to the Viewmodel of the Keyboard.
<Window.Resources>
<DataTemplate DataType="{x:Type viewModel:KeyboardViewmodel}">
<view:KeyboardView/>
</DataTemplate>
</Window.Resources>
<Grid>
<Border Background="White">
<HeaderedContentControl Content="{Binding Path=CurrentPage}"/>
</Border>
<TextBox Width="120"/>
</Grid>
I now have the OnScreenKeyboard and a Textbox in my Window. Clicking into the Textbox and pressing buttons of my OnScreenKeyboard will result in text appearing in my Textbox. All Controls within the KeyboardView are set to avoid getting focus. This is necessary to maintain focus on the Textbox.
The Buttons in the KeyboardView all bind to the Command ClickCommandin my KeyboardViewmodel. Here is the code of the KeyboardViewmodel:
public class KeyboardViewmodel : BaseModel
{
public BaseCommand ClickCommand { get; set; }
public KeyboardViewmodel()
{
ClickCommand = new BaseCommand(PressAndRelease);
}
public void PressAndRelease(object key)
{
if (((string)key).Length <= 1)
SendKeys.SendWait((string)key);
else
SendKeys.SendWait("{" + (string)key + "}");
}
}
Now I did create a NuGet Package with these Files and imported them to the project where I want to use my OnScreenKeyboard.
I did do basically the same as when I tested the OnScreenKeyboard before.
But let me explain the structure of the project a little more:
I have a MainView + MainViewmodel. The MainViewmodel manages the navigation between available pages. To show these pages I have - as in the short example before - a ContentControl whose content is bound to a CurrentPage Property. The MainViewis a normal Window, all other Views are UserControls.
In one of these pages I need an OnScreenKeyboard (DetailsView + DetailsViewmodel). So it seemed logical to me to use another ContentControl within the DetailsView:
<Border Grid.Column="0" Grid.Row="4" Grid.ColumnSpan="3" Height="Auto" Width="Auto">
<ContentControl Content="{Binding Path=OnScreenKeyboard}"/>
</Border>
I create the KeyboardViewmodel in the constructor of the DetailsViewmodel. The constructor of the DetailsViewmodel is called in the MainViewmodel at startup.
So now everything works out fine so far, the OnScreenKeyboard is shown on the correct page in the correct place. If I click a button of the OnScreenKeyboard the proper bound command is called and the SendKeys.SendWait() Method is called.
But no text appears in the TextBox. I have a very bad understanding of the SendKeys.SendAwait() Method. Also, the MSDN Documentation seems to be not very exhaustive on this topic.
It states: "Sends the given keys to the active application, and then waits for the messages to be processed."
Now. The Active / Focused Application is my Application. So my guess is that the KeyStrokes should be processed by my Textbox.
My Questions:
Any guesses how to debug the 'SenWait()' Method further e.g. track where the strokes are really sent to or something like that?
Is this the correct way for sending KeyStrokes to an active Application? It seems like SendKeys comes from Windows Forms, I use WPF.
Should I just pass my Textbox as reference to the OnScreenKeyboard and write directly to the referenced Textbox? This would make me much less flexible in regards of reusability.
Update:
As pointed out in the comments this could probably be a duplicate question.
I am well aware of the various different solutions and have already considerd them:
http://wpfkb.codeplex.com/
http://www.codeproject.com/Articles/32568/A-Touch-Screen-Keyboard-Control-in-WPF
http://www.codeproject.com/Articles/145579/A-Software-Virtual-Keyboard-for-Your-WPF-Apps
But as one may understand these projects are looking all way too powerfull for my simple needs.
Here a screenshot to provide a better understanding of my needs:
It is really as simple as that. 4 rows of buttons that will never change, no other controls / functionality than sending the CommandParameter of the pressed button to the Textbox / Active Form.
Researching on that specific problem hasn't shown any problems like that. In most other SO Questions the problem is to send Data to another Window, not to send Data WITHIN the current Window.
So I don't consider this question as duplicate.