Slider MouseLeftButtonDown Doesn't Work? - wpf

When trying to use a Slider control I'd like to listen to the MouseLeftButtonDown and MouseLeftButtonUp. I have handlers set up for both events. The MouseLeftButtonUp works as expected. MouseLeftButtonDown is not raised at all.
Any ideas why?
I've done a bit of googling and it seems that the WPF doesn't fire either. One of the solutions (in this post) was to use the Preview version of the events which is something silverlight doesn't support.
Is there any simple solution to this that I'm not seeing?
Thanks
J

It happens because Slider handles mouse down/up events. Internally its implemented as two RepeatButtons and a thumb in the middle. When you click on left or right side of the slider your mouse events are handled by RepeatButtons, and you don't get them.
If you still want to handle handled event you can use AddHandler() method. Here is Silverlight example:
XAML
<Slider Width="100"
VerticalAlignment="Top"
Minimum="0"
Maximum="100"
Name="sl" />
C#
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
sl.AddHandler(MouseLeftButtonDownEvent, new MouseButtonEventHandler(Slider_MouseLeftButtonDown), true);
sl.AddHandler(MouseLeftButtonUpEvent, new MouseButtonEventHandler(Slider_MouseLeftButtonUp), true);
}
private void Slider_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
}
private void Slider_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
}
}
In WPF situation is almost same (small differences in names).

Related

Button click event not responding after collapsing parent

I have a UserControl with a number of StackPanel's. I like to hide specific panels depending on the user action. A StackPanel which is visible on startup gives me a number of working buttons. The buttons have click events in the code behind file. After collapsing the panel and then making it visible again the buttons no longer work. Here is a part of my UserControl:
<StackPanel x:Name="buttonPanel" Orientation="Horizontal">
<Button x:Name="ReMindNodeNotes" Content=""
FontFamily="Segoe UI Symbol" FontSize="14" Foreground="#FF292323"
HorizontalAlignment="Left" BorderThickness="1" Padding="0"
UseLayoutRounding="True" Click="NoteClicked" />
<Button x:Name="ReMindNodeRemove" Content=""
FontFamily="Segoe UI Symbol" FontSize="14" Foreground="#FF292323"
HorizontalAlignment="Left" BorderThickness="1" Padding="0"
UseLayoutRounding="True" Click="RemoveClicked" />
</StackPanel>
And here is the code (for now just some text):
private void NoteClicked(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("NoteClicked...");
}
private void RemoveClicked(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine("RemoveClicked...");
}
I have been looking for a solution the last two days. No luck so far. Who can help...?
THX Peter
Follow up 1...
Here is the code for collapsing the panel:
private void MoreClicked(object sender, RoutedEventArgs e)
{
System.Diagnostics.Debug.WriteLine(this.nodeName);
this.buttonPanel.Visibility =
this.buttonPanel.Visibility ==
Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
}
It works if the buttonPanel has focus. If the focus is on another panel it does not. Furthermore, what I probably should have mentioned... is that users can create multiple instances of the user control.
THX
Follow up 2...
I continue working on a solution of course... ;-) and I found a solution, which however is not the solution I want. Let me explain.
Users can interactively create multiple instances of the user control mentioned before. When a new instance is created, that instance gets focus. Now every instance has its own set of buttons which are on a stackpanel. When the focus goes to another instance I want the panel of the previous instance to collapse. The focus should then be set to the new (or selected existing) instance.
When I do this manually, it works! When I try to achieve this through the GotFocus and LostFocus events however, it does not. Here is the code for the manual solution (which works):
private void MoreClicked(object sender, RoutedEventArgs e)
{
this.buttonPanel.Visibility =
this.buttonPanel.Visibility ==
Visibility.Visible ? Visibility.Collapsed : Visibility.Visible;
}
Here are the LostFocus and GotFocus events:
private void NodeGotFocus(object sender, RoutedEventArgs e)
{
this.buttonPanel.Visibility = Visibility.Visible;
}
private void NodeLostFocus(object sender, RoutedEventArgs e)
{
this.buttonPanel.Visibility = Visibility.Collapsed;
}
I really appreciate your help! THX again...
Thanks for your sample morincer. The problem however is a little more complex. Let me try to explain the solution which I found after some more research. Maybe other developers can benefit from it as well.
I added the GotFocus and LostFocus events to my userconctrol. If I click somewhere inside the usercontrol the focus changes every time. Strange as these events are only defined on the usercontrol itself and not it's children. I have several buttons and a textbox inside the usercontrol and when I for example click on one of the buttons of the usercontrol that has focus the LostFocus and GotFocus events are fired for usercontrol anyway.
The most important event for me in this case is the LostFocus event. When the usercontrol looses focus - for example to another control - I want the button panel to disappear. Since the LostFocus event fires every time a object inside the usercontrol is touched, I cannot distinguish between the situation in which I want to hide and show the buttons.
I got a little closer to a solution by changing the LostFocus event as follows:
private void LostFocus(object sender, RoutedEventArgs e)
{
Object fo = FocusManager.GetFocusedElement();
if (fo.GetType().ToString().Contains("TextBox") ||
fo.GetType().ToString().Contains("ScrollViewer"))
{
this.buttonPanel.Visibility = Visibility.Collapsed;
}
}
This covers most of the situations. When the cursor is positioned in the TextBox the button panel is closed. The button panel is also closed when the user clicks on the background. This seems to be a ScrollViewer (found through debugging the code). Can anyone explain this...?
The situation which is not covered however, is when a user clicks on another usercontrol. It does of course when the user clicks on the TextBox (see the code) but not when the user clicks on a button. I tried to compare sender and FocusManager.GetFocusedElement(). Problem is that the sender returns the usercontrol (which is what I am looking for) but the FocusManager.GetFocusedElement() returns the button that was pressed. Now I could ask for it's parent which is a border then ask for the borders parent which is a stack panel and so on until I arrive at the usercontrol. A code behind file however was introduced with the idea to split design and logic while this solution would tie them together again. If I would change the XAML I would have to change the logic as well. Doesn't seem to be the right solution to me.
I found a solotion by giving every usercontrol a unique name in the constructor. I then give all the buttons unique names as well (I don't use them in my code anyway) starting with the name of the usercontrol. This then gives me the possibility to compare names at runtime and determine whether the focus has changed to another instance of the usercontrol. Here is the code:
private void NodeLostFocus(object sender, RoutedEventArgs e)
{
Object fo = FocusManager.GetFocusedElement();
if (fo.GetType().ToString().Contains("ScrollViewer"))
{
this.buttonPanel.Visibility = Visibility.Collapsed;
}
else if (fo.GetType().ToString().Contains("TextBox"))
{
if (!((TextBox)fo).Name.Contains(this.nodeName))
{
this.buttonPanel.Visibility = Visibility.Collapsed;
}
}
else if (fo.GetType().ToString().Contains("Button"))
{
if (!((Button)fo).Name.Contains(this.nodeName))
{
this.buttonPanel.Visibility = Visibility.Collapsed;
}
}
}
Now this works! But…I don't like the solution. I am depending on names instead of a good architecture. Does anyone hove an idea how to compare the actual sender with the usercontrol that is the parent of the button pressed (FocusManager.GetFocusedElement())? Or any other solution that relies on good programming?
THX again

Silverlight: How to Prevent Routing a MouseMove Event from a Child Canvas to Its Parent Canvas

I have my XAML code:
<Canvas x:Name="mainCanvas" Width="200" Height="150" Background="LightGray"
MouseLeftButtonUp="mainCanvas_MouseLeftButtonUp"
MouseMove="mainCanvas_MouseMove">
<Canvas x:Name="topCanvas" Width="200" Height="100" Background="LightBlue"
MouseLeftButtonUp="topCanvas_MouseLeftButtonUp"
MouseMove="topCanvas_MouseMove">
</Canvas>
</Canvas>
and its code behind:
private void topCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("topCanvas_MouseLeftButtonUp");
e.Handled = true; // This can prevent routing to the mainCanvas
}
private void mainCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
MessageBox.Show("mainCanvas_MouseLeftButtonUp");
}
private void topCanvas_MouseMove(object sender, MouseEventArgs e)
{
Debug.WriteLine("topCanvas_MouseMove");
// How to prevent routing to the mainCanvas?
// e.Handled = true does NOT exist in MouseEventArgs
}
private void mainCanvas_MouseMove(object sender, MouseEventArgs e)
{
Debug.WriteLine("mainCanvas_MouseMove");
}
My question is already in the comments.
How to prevent routing the MouseMove event from the topCanvas (the child canvas) to the mainCanvas (parent canvas)?
Thanks.
Peter
Try setting the IsHitTestVisible property of your Canvas. With that property set accordingly mouse events will go either "through" your control or will be caught by it.
Hope this is what you need.
You can try comparing e.OriginalSource in mainCanvas's MouseMove Event and exit the Sub if it wasn't originated from the mainCanvas.
private void mainCanvas_MouseMove(object sender, MouseEventArgs e)
{
if (sender != e.OriginalSource)
return;
}
In replying to your comment in a little more detail. According to the UIElement.MouseMove Event MSDN link.
Controls that inherit MouseMove can provide handling for the event
that acts as handler for all instances, by overriding the OnMouseMove
method. As with direct handling of the event, there is no Handled
property available, so OnMouseMove cannot be implemented in such a way
that it suppresses further handling of the event through the Handled
technique.
and this link states:
This event creates an alias for the Mouse.MouseMove attached event for
this class
Which brings us to this link on AttachedEvents which states.
Extensible Application Markup Language (XAML) defines a language
component and type of event called an attached event. The concept of
an attached event enables you to add a handler for a particular event
to an arbitrary element rather than to an element that actually
defines or inherits the event. In this case, neither the object
potentially raising the event nor the destination handling instance
defines or otherwise "owns" the event.
So as I see it, your only option is to code around it.
The functionality is called "Event Bubbling". You can stop it using below code:
jQuery:
event.stopPropagation();
Ref: http://api.jquery.com/event.stopPropagation/
You can also try below code:
e.stopPropagation(); //to prevent event from bubbling up
e.preventDefault(); //then cancel the event (if it's cancelable)
Ref: http://stackoverflow.com/questions/1967537/how-to-stop-event-bubbling-with-jquery-live
Thanks,
Ravi Verma

WPF- Is it possible to add an OnVerticalOffsetChanged event to a custom textbox?

Is there any way for me to do this?
You can tell when the VerticalOffset changes by adding a handler to the ScrollViewer.ScrollChanged event to your TextBox. Something like this:
<TextBox AcceptsReturn="True" ScrollViewer.ScrollChanged="TextBox_ScrollChanged" />
The TextBox uses a ScrollViewer internally, so it's ScrollChanged event will bubble up to the TextBox (where you can handle it). The event arguments include information about what changed, such as the VerticalChange (the amount that the control has scrolled vertically).
private void TextBox_ScrollChanged(object sender, ScrollChangedEventArgs e) {
System.Diagnostics.Debug.WriteLine(string.Format("************ {0}", e.VerticalChange));
}

How to set focus on TextBox in Silverlight 4 out-of-browser popup

I have a simple ChildWindow popup in Silverlight 4 (beta).
Important: This is an out-of-browser application.
i want to auto set focus on a TextBox control when the window opens.
I've tried a couple things :
The following code doesn't seem to do anything. I don't think the control is ready to be focussed after 'Loading'.
private void ChildWindow_Loaded(object sender, RoutedEventArgs e)
{
textBox1.Focus();
}
This works, but its klunky.
private void ChildWindow_GotFocus(object sender, RoutedEventArgs e)
{
if (_firstTime == true) {
textBox1.Focus();
_firstTime = false;
}
}
Isn't there a better way? I always had to do horrible things like this in WinForms but was hoping not to have to anymore.
Note: This similar question is for in browser only. It suggests calling System.Windows.Browser.HtmlPage.Plugin.Focus(); which doesn't work and in fact gives an error when running on Silverlight 4 beta out-of-browser.
I was having the same problem in SilverLight 4 (OOB) and I noticed that the tab sequence would set focus to a control that i could not see. What appears to be happening is the focus is being set to your control (first one in the tab sequence) and then for some reason the focus moves to the ContentControl (name ="content"), which (i think) is the parent of the child window.
ContentControl by default has IsTabStop=true.
see....
Why would I want IsTabStop set to true on a ContentControl?
To set the ContentControl.IsTabStop = false for all ContentControls in your app, add this to your styles.xaml.
<Style TargetType="ContentControl" >
<Setter Property="IsTabStop" Value="false"/>
</Style>
The same issue happens with the tab sequence on the MainPage. This style will also fix this.
You are on the right track. You need to handle for two test cases:
1. Setting the focus in the browser.
2. Setting the focus out of the browser.
Your code you that you showed in the Loaded event will work perfectly fine out of the browser. All that is necessary is to refactor it to handle both cases:
private void ChildWindow_Loaded(object sender, RoutedEventArgs e)
{
if (App.current.IsRunningOutOfBrowser)
{
textBox1.Focus();
}
else
{
System.Windows.Browser.HtmlPage.Plugin.Focus();
textBox1.Focus();
}
}
That should do the trick for you.
Thanks for all the posts but after doing a little research the below thing work for me
in Xamal:
<TextBox VerticalAlignment="Center" HorizontalAlignment="Left" FontFamily="Arial" FontSize="12" Height="25" Width="200" Margin="38,50,0,0" Name="txtUserName" Text="{Binding LoginInfo.UserName,Mode=TwoWay, NotifyOnValidationError=True}" IsTabStop="True" TabIndex="1" ></TextBox>
// Initialiazing Main Part View Model
/// </summary>
/// <param name="mainPartViewModel"></param>
public ChildWindowLoginControl(MainPartViewModel mainPartViewModel)
{
InitializeComponent();
this.DataContext = mainPartViewModel;
System.Windows.Browser.HtmlPage.Plugin.Focus();
this.GotFocus += (s, e) => { txtUserName.Focus(); };
}
I had to use your GotFocus way for Silverlight 3 application written in IronPython when I wanted to set focus in ChildWindow.
I use:
protected override void OnOpened()
{
base.OnOpened();
textBox1.Focus();
}
Thanks for all the post, but i have find the work done through following.
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
if (App.Current.IsRunningOutOfBrowser)
{
txtSalesOrderNo.Focus();
}
else
{
System.Windows.Browser.HtmlPage.Plugin.Focus();
txtSalesOrderNo.Focus();
}
}

Synchronizing scroll positions for 2 WPF DataGrids

I am trying to synchronize the horizontal scroll position of 2 WPF DataGrid controls.
I am subscribing to the ScrollChanged event of the first DataGrid:
<toolkit:DataGrid x:Name="SourceGrid" ScrollViewer.ScrollChanged="SourceGrid_ScrollChanged">
I have a second DataGrid:
<toolkit:DataGrid x:Name="TargetGrid">
In the event handler I was attempting to use the IScrollInfo.SetHorizontalOffset, but alas, DataGrid doesn't expose IScrollInfo:
private void SourceGrid_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
((IScrollInfo)TargetGrid).SetHorizontalOffset(e.HorizontalOffset);
// cast to IScrollInfo fails
}
Is there another way to accomplish this? Or is there another element on TargetGrid that exposes the necessary IScrollInfo to achieve the synchronization of the scroll positions?
BTW, I am using frozen columns, so I cannot wrap both DataGrid controls with ScrollViewers.
There is great piece of code to do this:
http://www.codeproject.com/KB/WPF/ScrollSynchronization.aspx
According to the Microsoft product group, traversing the visual tree to find the ScrollViewer is the recommended method, as explained in their answer on Codeplex.
We had this same problem when using the Infragistics grid because it didn't (still doesn't) support frozen columns. So we had two grids side-by-side that were made to look as one. The grid on the left didn't scroll horizontally but the grid on the right did. Poor man's frozen columns.
Anyway, we ended up just reaching into the visual tree and pulling out the ScrollViewer ourselves. Afterall, we knew it was there - it just wasn't exposed by the object model. You could use a similar approach if the WPF grid does not expose the ScrollViewer. Or you could subclass the grid and add the functionality you require to make this work.
Interested in hearing why you need to do this.
This is a great solution. Worked fine for me in WPF.
http://www.codeproject.com/Articles/39244/Scroll-Synchronization
I just made a reference to ScrollSynchronizer dll, added a xml import:
xmlns:scroll="clr-namespace:ScrollSynchronizer"
then just added this to both my datagrids and bobs your uncle:
<DataGrid.Resources>
<Style TargetType="ScrollViewer">
<Setter Property="scroll:ScrollSynchronizer.ScrollGroup" Value="Group1" />
</Style>
</DataGrid.Resources>
You can trick the datagrid to expose its ScrollViewer as public property for each grid, when for example innerGridControl_ScrollChanged() handler called during initialisation of the usercontrol.
To expose it you can make your grid in an xaml View file, and then compose two of them in another xaml View.
Below code is on the innerGrid.xaml.cs for example:
public ScrollViewer Scroller { get; set; } // exposed ScrollViewer from the grid
private bool _isFirstTimeLoaded = true;
private void innerGridControl_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (_isFirstTimeLoaded) // just to save the code from casting and assignment after 1st time loaded
{
var scroller = (e.OriginalSource) as ScrollViewer;
Scroller = scroller;
_isFirstTimeLoaded = false;
}
}
on OuterGridView.xaml put an attached event handler definition:
<Views:innerGridView Grid.Row="1" Margin="2,0,2,2" DataContext="{Binding someCollection}"
x:Name="grid1Control"
ScrollViewer.ScrollChanged="Grid1Attached_ScrollChanged"
></Views:innerGridView>
<Views:innerGridView Grid.Row="3" Margin="2,0,2,2" DataContext="{Binding someCollection}"
x:Name="grid2Control"
ScrollViewer.ScrollChanged="Grid2Attached_ScrollChanged"
></Views:innerGridView>
then access that public ScrollViewer.SetHorizontalOffset(e.HorizontalOffset) method when another scrolling event occur.
Below code is in the OuterGridView.xaml.cs on one of the handler definition (
private void Grid1Attached_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e != null && !e.Handled)
{
if (e.HorizontalChange != 0.0)
{
grid2Control.Scroller.ScrollToHorizontalOffset(e.HorizontalOffset);
}
e.Handled = true;
}
}
private void Grid2Attached_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e != null && !e.Handled)
{
if (e.HorizontalChange != 0.0)
{
grid1Control.Scroller.ScrollToHorizontalOffset(e.HorizontalOffset);
}
e.Handled = true;
}
}
Also make sure any other scroll_changed event inside the inner grid (if any, for example if you define a TextBox with default scroller in one of the column data template) has its e.Handled set to true to prevent outer grid's handler processing it (this happened due to default bubbling behaviour of routedevents). Alternatively you can put additional if check on e.OriginalSource or e.Source to filter the scroll event you're intended to process.

Resources