Attach to Event of Sibling Control in Custom WPF Control Initialization

I am trying to create a WPF custom slider control that acts as a scrollbar for a Listview. I'm doing this by putting the name of the listview in the Tag attribute of my custom slider and then using the slider's OnValueChange event to scroll the listview. This works great, however, when I scroll in the listview with my mousewheel the slider doesn't move. What I need is a way to attach a method to the listview's MouseWheel event when my custom slider initializes. Here is what I've tried:

While it seems like OnMouseWheel should fire when the I scroll in the listview, it's not happening and I haven't been able to find anything else to try. Is there a way to do what I want in WPF? I know I could put a method in the code behind of my view to make the MouseScroll event of the listview move the slider, but I was hoping to encapsulate as much of the logic for the slider in the slider class as possible.

Best How To :

So it seems that the trick was to use PreviewMouseWheel instead of MouseWheel. For future reference here is my current class:

/// <summary>
/// This class creates a custom control that can be used as a scrollbar for a listbox that displays the current
/// group visible in the listbox on a small label next to the slider thumb.
///
/// To use it, set the Tag value to the name of the listbox the scollbar will be controlling.
/// </summary>
public class LabeledScrollbar : Slider
{
//Tracks control initialization to ensure it only gets loaded once
private bool initialized = false;
//The listview this slider will control
private ListView listView;
public LabeledScrollbar(): base()
{
this.Loaded += LabeledScrollbar_Loaded;
}
void LabeledScrollbar_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
//The tag must be set to the name of a listbox
listView = (ListView)this.FindName(this.Tag.ToString());
if (listView != null && !this.initialized)
{
//Make sure that the mouse wheel event in the linked listbox is handled
listView.PreviewMouseWheel += (s, ev) =>
{
if (ev.Delta > 0)
this.Value += 3;
else
this.Value -= 3;
};
//Move scrollbar and list to the top if the collection changes
((INotifyCollectionChanged)listView.Items).CollectionChanged += (s, ev) =>
{
this.Maximum = ((ItemCollection)listView.Items).Count - 1;
this.Value = this.Maximum;
};
//Get the max value of the slider by checking the tag value and looking up the associated listbox
this.Maximum = ((ItemCollection)listView.Items).Count - 1;
this.Value = this.Maximum;
this.initialized = true;
}
}
protected override void OnValueChanged(double oldValue, double newValue)
{
//Refresh the tickbar so that it will render for a new value
InvalidateTickbar();
//Scroll the list box to the correct location
ScrollToIndex(newValue);
}
private void ScrollToIndex(double newValue)
{
if (newValue == this.Maximum)
{
//ScrollIntoView method does not scroll to the top so
//we need to access the scrollview to move the slider to the top
if (VisualTreeHelper.GetChildrenCount(listView) > 0)
{
var chrome = VisualTreeHelper.GetChild(listView, 0);
var scrollView = (ScrollViewer)VisualTreeHelper.GetChild(chrome, 0);
scrollView.ScrollToTop();
}
}
else
{
var collection = (CollectionView)CollectionViewSource.GetDefaultView(listView.ItemsSource);
var index = (collection.Count - 1) - (int)Math.Floor(newValue);
var selectedItem = collection.GetItemAt(index);
listView.ScrollIntoView(selectedItem);
}
}
private void InvalidateTickbar()
{
//update the tickbar for the new position
if (VisualTreeHelper.GetChildrenCount(this) > 0)
{
var firstChild = VisualTreeHelper.GetChild(this, 0);
if (VisualTreeHelper.GetChildrenCount(firstChild) > 0)
{
var secondChild = (CustomTickBar)VisualTreeHelper.GetChild(firstChild, 0);
secondChild.InvalidateVisual();
}
}
}
}

To have such a result, your grid must be really small. I see at least 3 solutions to your issue: 1- Make your grid a bit bigger until it fits. 2- Put 2 columns in your grid, you'd put the number on the left column and the % on the...

I found a way of doing it, with code behind. I put my Expander in a Canvas and I added to my Expander two events: Expanded="searchMenuExpander_Expanded" and Collapsed="searchMenuExpander_Collapsed" which are defined as: private void searchMenuExpander_Expanded(object sender, RoutedEventArgs e) { Canvas.SetZIndex(searchMenuCanvas, 99); } private void searchMenuExpander_Collapsed(object sender, RoutedEventArgs e) { Canvas.SetZIndex(searchMenuCanvas,...

You can utilize the utilize the PrintDocument class, which is not WPF specific. This class allows you to send output to a printer. The PrintPage event should be handled, where you utilize the PrintPageEventArgs to obtain a Graphics context; which is used to draw the exam to the printer output....

If you declare a Style without an x:Key, it will override the default style for that control. <Style TargetType="local:CustomControl"> So the code above will effect all CustomControl elements throughout the entire application (or within the scope). If you do not want to override the base style, you can give your...

But you aren't binding a list of strings to a ListView, you are binding an IEnumerable<String> to a TextBlock.Text field, when it is expecting a String as you can see in the errors. The fastest way to solve your problem is to change the line to this.AInfoLv.Items.Add(new { Label=" "...

The very first line in the remarks section of the documentation for the CollectionView class says: You should not create objects of this class in your code. So, I am guessing it is probably not designed to be used the way you are using it. I always use CollectionViewSource.GetDefaultView(collection) (which...

In WPF, you can't even draw something in pixel units without at least some extra effort. WPF uses "device independent units" where each unit is 1/96th of an inch. Even that is only a theoretical relationship, as it depends on the display device correctly reporting its resolution, which in turn...

WPF's ItemsControl is the correct way to show items in your views when the number of items can change. Many controls inherit from ItemsControl, like the ComboBox itself, or the DataGrid, or the ListBox... But in this example I'll use ItemsControl directly, since what you're trying to do doesn't need...

if you want to use same Click handler for two button, try this: private void btnDashboard_Click(object sender, RoutedEventArgs e) { Button btnSender = sender as Button; if(btnSender.Name == "btn1") { //Code for Button1 } else { //Code for Button2 } } Also make sure each button has a Name property...

You can implement your OptionalPhoneAttribute based on the original PhoneAttribute: public sealed class OptionalPhoneAttribute : ValidationAttribute { public override bool IsValid(object value) { var phone = new PhoneAttribute(); //return true when the value is null or empty //return original IsValid value only when value is not null or empty return...

just hold the color values in a config file simple text file will suffice. though you can use VisualStudio Resource file.. file will contain lines in each: item_enum_name item_type item_value for example: main_screen_bg_color Color Black company_logo URI \logos\logo1.jpg and so on.. just load the file parse it and use bind...

That is not the correct way to register a dependency property. public bool UseCustomTooltips { get { return (bool)GetValue(UseCustomTooltipsProperty); } set { SetValue(UseCustomTooltipsProperty, value); } } public static readonly DependencyProperty UseCustomTooltipsProperty = DependencyProperty.Register("UseCustomTooltips", typeof(bool), typeof(MyControl), new PropertyMetadata(false, new PropertyChangedCallback(MyCallbackMethod))); Use the propdp snippet, it really is a beautiful thing....

These Styles reference other Styles and Templates via StaticResource: MyFocusVisualStyte (Typo? Shouldn't it be MyFocusVisualStyle?), SliderThumbStyle, SliderRepeatButtonStyle, HorizontalSlider, VerticalSlider... All these Styles and Templates must exist and be defined BEFORE they're used. In this case, the resources must be defined in this order (I'll copy only the first node of...

Your answer can be found in the Dependency Property Value Precedence page on MSDN. In short, you have set a local value on the IsOpen property and that has a higher precedence than the value set by the Trigger. The solution is to not set the local value, but to...

The current Band SDK does not support Windows desktop (i.e. Win32) applications. It supports only Windows Store and Windows Phone (i.e. WinRT) applications. Portable libraries can be confusing as the terms '.NETCore' and 'netcore451' refer to the Windows Store version of the .NET framework....

@Clemens answer from his comment on the original question provided the solution. Ensuring that the file stream was being closed responsibly and changing the BitmapCacheOption to OnLoad now shows each image in the asynchronous load. The final code for the asynchronous load looks like: private void LoadAsync(object sender, DoWorkEventArgs e)...

In order to observe changes in a collection WPF provides the ObservableCollection class which implements INotifyCollectionChanged and INotifyPropertyChanged. Replace List<T> with ObservableCollection<T> and your code should work.

System.Windows.Interactivity.dll is not in the GAC, so .Net doesn't know where to find it. By adding a reference to your main project, you make the build system copy the DLL to the output folder. This lets the runtime find it....

As @goobering already mentioned, this is a very broad set of questions, I will attempt to guide you in the right direction here. How can I parse data from pages? I would recommend starting with RSS. Practically all news websites have some kind of RSS feed that you can tap...

Your issue is nothing to do with Animation.The problem is you are comparing sender.Type while you should compare sender itself i.e. use if (sender is TabItem) instead of if (obj is TabItem). Moreover, There is no need to compare sender with TabItem, Lable, Window and etc one by one, they...

Given your example data where the first number is always a single digit: allScenes.OrderBy(x => x.SceneNumber).ToList() If you can possibly have multi-digit numbers, please provide them and where you want them in the sort order. This is one way to sort multiple digit numbers: var allScenes = new[]{ new {SceneNumber="4B"},...

Finally, I got the answer from https://github.com/cefsharp/CefSharp/issues/1080#issuecomment-111969127 I am sorry post the duplicate thread online. With the amaitland's help. I resolved this issue. We just needed remove OnMouseLeftButtonDown method. Thanks for your time....

I'm going to attempt to answer your questions directly, however there is much that can be discussed here. What this template doing? All controls have some kind of default template, a pre-defined look and feel of what the control looks like. The Template is overriding the default look and feel...

You just have to check if it starts with "price". Note that I don't think that ToString() is appropriate; you should rather implement IComparer<T> and strongly type your objects in your listbox. public int Compare(object x, object y) { // test for equality if (x.ToString() == y.ToString()) { return 0;...

You may use a DoubleAnimation instead of an ObjectAnimationUsingKeyFrames. By only setting its To property, but not From, the animation starts from the current property value. It requires that you also set an initial value of the Height of the Border: <Border Height="20" ...> <Border.Triggers> <EventTrigger RoutedEvent="MouseLeftButtonUp"> <BeginStoryboard> <Storyboard> <DoubleAnimation...

You can use a Popup control, coupled with it's Placement property to display your popup based on the current location of your buttons. <Popup Placement="Top" PlacementTarget="{Binding ElementName=yourButton}" ... /> Inside your Popup, you can place your UserControl or any content element which will act as the popup's content. See here...

As usual the answer is ain't common, it depends on how you really want to implement it. But for the sake of patterns and organized code, i would recommend that you have: Separate Viewmodel per view: each viewmodel should only contain as many data as many you want to display/work...

Instead of a static counter you should have a view model with a collection of Person objects public class ViewModel { public ObservableCollection<Person> Persons { get; set; } } and bind the ItemsSource property of the ListView to this collection. <ListView ItemsSource="{Binding Persons}"> ... </ListView> You could now bind to...