Accessing parent from UserControl in Silverlight - silverlight

From inside a UserControl I'm trying to reference a method on its parent.
public partial class Tab3_2Data : UserControl
{
public Tab3_2Data()
{
InitializeComponent();
//MainPage mp = this.Ancestors().OfType<MainPage>().FirstOrDefault();
//var x = VisualTreeHelper.GetParent(this);
//var z = this.Parent;
//var parent = this.Ancestors().Take(1).FirstOrDefault();
// None of the above work.. all come back as null
// Trying to access this method on the parent
//ShowMessage("test", OperationStatus.Green);
Have tried this yet nothing
and
// only available OOB
//mainPage = System.Windows.Application.Current.RootVisual as MainPage;
UserControl being called like:
<!-- Tab 3_2 -->
<controls:TabItem Header="Groups and Roles">
<UserControls:Tab3_2Data />
</controls:TabItem>
EDIT2:
This is how I got it working:
In the UserControl:
UserControl x:Class="xyz.ClientApp.UserControls.Tab3_2Data" Loaded="Tab3_2Data_OnLoaded"
then in code behind:
private void Tab3_2Data_OnLoaded(object sender, RoutedEventArgs e)
{
mp = this.Ancestors().OfType<MainPage>().FirstOrDefault();
//mp.ShowMessage("test", OperationStatus.Green);
}
which uses the VisualTreeEnumeration helper class referenced in link above.
public static class VisualTreeEnumeration
{
public static IEnumerable<DependencyObject> Descendents(this DependencyObject root, int depth)
{
int count = VisualTreeHelper.GetChildrenCount(root);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(root, i);
yield return child;
if (depth > 0)
{
foreach (var descendent in Descendents(child, --depth))
yield return descendent;
}
}
}
public static IEnumerable<DependencyObject> Descendents(this DependencyObject root)
{
return Descendents(root, Int32.MaxValue);
}
public static IEnumerable<DependencyObject> Ancestors(this DependencyObject root)
{
DependencyObject current = VisualTreeHelper.GetParent(root);
while (current != null)
{
yield return current;
current = VisualTreeHelper.GetParent(current);
}
}
}

I believe the parent will only be available when the control is loaded. You can try to run this logic in a handler for the UserControl.Loaded event.
In addition, as the control is child of a TabItem, it will only be loaded when the TabItem is visible.

Related

How to handle items added to Attached Property of ObservableCollection type

I have a UWP project that uses MapControl, which is a sealed class - cant derive a new class from it.
Trying to make a bindable Attached Property, which would have access to MapControl.Children.
The problem is that it only works when I set ViewModel's collection, but not when I add a new element to that collection:
// Works fine
this.MapChildrenExtCollection = new ObservableCollection<MapChildElement>();
// Nothing happens
this.MapChildrenExtCollection.Add(new MapChildElement());
Heres my code for the Attached Property:
namespace UWPMap.Extensions
{
public class MapControlExt : DependencyObject
{
public static readonly DependencyProperty ChildrenExtProperty = DependencyProperty.Register(
"ChildrenExt",
typeof(ObservableCollection<MapChildElement>),
typeof(MapControlExt),
new PropertyMetadata(new ObservableCollection<MapChildElement>(), ChildrenExtPropertyChanged));
public ObservableCollection<MapChildElement> ChildrenExt
{
get { return (ObservableCollection<MapChildElement>)GetValue(ChildrenExtProperty); }
set { SetValue(ChildrenExtProperty, value); }
}
public static void SetChildrenExt(UIElement element, ObservableCollection<MapChildElement> value)
{
element.SetValue(ChildrenExtProperty, value);
}
public static ObservableCollection<MapChildElement> GetChildrenExt(UIElement element)
{
return (ObservableCollection<MapChildElement>)element.GetValue(ChildrenExtProperty);
}
private static void ChildrenExtPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var control = (MapControl)obj;
var oldCollection = e.OldValue as INotifyCollectionChanged;
var newCollection = e.NewValue as INotifyCollectionChanged;
if (oldCollection != null)
{
oldCollection.CollectionChanged -= Extensions.MapControlExt.ChildrenExtCollectionChanged;
}
if (newCollection != null)
{
oldCollection.CollectionChanged += Extensions.MapControlExt.ChildrenExtCollectionChanged;
}
ManageChildrenExt();
}
static void ChildrenExtCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
ManageChildrenExt();
}
static private void ManageChildrenExt()
{
// Access MapControl.Children here
}
}
}
XAML:
<maps:MapControl x:Name="MyMap"
ext:MapControlExt.ChildrenExt="{x:Bind Path=MapChildrenExtCollection, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
</maps:MapControl>
The problem is that you are not adding the event handler to the new collection and using oldCollection variable by mistake.
The following snippet:
if (newCollection != null)
{
oldCollection.CollectionChanged += //here you have oldCollection by mistake
Extensions.MapControlExt.ChildrenExtCollectionChanged;
}
Should be:
if (newCollection != null)
{
newCollection.CollectionChanged +=
Extensions.MapControlExt.ChildrenExtCollectionChanged;
}

Scroll animation

How can I animate the the scrolling for ListBox? I know I can use scrollIntoView but how can I animate it? I want to press the arrow keys to move from one listBoxItem to another.
Here is a rough implementation based on the same approach as the following link
http://aniscrollviewer.codeplex.com/
The VerticalOffset property is read-only so instead you can use an attached property VerticalOffset on the ScrollViewer which in turn does ScrollToVerticalOffset. This attached property can be animated.
You can also create an extension method for ItemsControl called AnimateScrollIntoView.
Call it like this
listBox.AnimateScrollIntoView(yourItem);
ScrollViewerBehavior
public class ScrollViewerBehavior
{
public static DependencyProperty VerticalOffsetProperty =
DependencyProperty.RegisterAttached("VerticalOffset",
typeof(double),
typeof(ScrollViewerBehavior),
new UIPropertyMetadata(0.0, OnVerticalOffsetChanged));
public static void SetVerticalOffset(FrameworkElement target, double value)
{
target.SetValue(VerticalOffsetProperty, value);
}
public static double GetVerticalOffset(FrameworkElement target)
{
return (double)target.GetValue(VerticalOffsetProperty);
}
private static void OnVerticalOffsetChanged(DependencyObject target, DependencyPropertyChangedEventArgs e)
{
ScrollViewer scrollViewer = target as ScrollViewer;
if (scrollViewer != null)
{
scrollViewer.ScrollToVerticalOffset((double)e.NewValue);
}
}
}
ItemsControlExtensions
public static class ItemsControlExtensions
{
public static void AnimateScrollIntoView(this ItemsControl itemsControl, object item)
{
ScrollViewer scrollViewer = VisualTreeHelpers.GetVisualChild<ScrollViewer>(itemsControl);
UIElement container = itemsControl.ItemContainerGenerator.ContainerFromItem(item) as UIElement;
int index = itemsControl.ItemContainerGenerator.IndexFromContainer(container);
double toValue = scrollViewer.ScrollableHeight * ((double)index / itemsControl.Items.Count);
Point relativePoint = container.TranslatePoint(new Point(0.0, 0.0), Window.GetWindow(container));
DoubleAnimation verticalAnimation = new DoubleAnimation();
verticalAnimation.From = scrollViewer.VerticalOffset;
verticalAnimation.To = toValue;
verticalAnimation.DecelerationRatio = .2;
verticalAnimation.Duration = new Duration(TimeSpan.FromMilliseconds(1000));
Storyboard storyboard = new Storyboard();
storyboard.Children.Add(verticalAnimation);
Storyboard.SetTarget(verticalAnimation, scrollViewer);
Storyboard.SetTargetProperty(verticalAnimation, new PropertyPath(ScrollViewerBehavior.VerticalOffsetProperty));
storyboard.Begin();
}
}
And since you also need to get a hold of the ScrollViewer you'll need this
public static class VisualTreeHelpers
{
public static T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
}
Take a look at this article, it explains how animate scrolling and add touch gestures. Download the source at the bottom of the page and look at WpfScrollContent solution. I would extend the WPF ListBox and add the scroll animation to it that way you can reuse the control.

WPF - reset ListBox scroll position when ItemsSource changes

I currently have a ListBox whose ItemsSource collection is bound to a property on my viewmodel, of type IEnumerable. When that preoprty's reference changes, the ListBox updates as expected, however I have a problem in that if I have a large collection of items and scroll to the bottom of the ListBox, and then change the reference to another collection containing, say, 1 item, the ListBox view is blank and no scrollbar is displayed. I have to then scroll the listbox up with the mouse wheel, until the 1 item comes into view.
So, what I think I'm after, is a way of resetting the scroll position of the ListBox to the top, whenever the ItemsSource property changes, so that something is always displayed no matter how large or small the collection.
I'm unable to reproduce your problem (for me, the ListBox is scrolled to the last item in the new collection when changing ItemsSource). Anyway, to scroll the ListBox to the top every time its ItemsSource changes you can use some code behind. First listen to changes in the ItemsSourceProperty and then scroll the ListBox to the top once its items has been generated
Update
Made an attached behavior that does this instead to avoid code behind. It can be used like this
<ListBox ...
behaviors:ScrollToTopBehavior.ScrollToTop="True"/>
ScrollToTopBehavior
public static class ScrollToTopBehavior
{
public static readonly DependencyProperty ScrollToTopProperty =
DependencyProperty.RegisterAttached
(
"ScrollToTop",
typeof(bool),
typeof(ScrollToTopBehavior),
new UIPropertyMetadata(false, OnScrollToTopPropertyChanged)
);
public static bool GetScrollToTop(DependencyObject obj)
{
return (bool)obj.GetValue(ScrollToTopProperty);
}
public static void SetScrollToTop(DependencyObject obj, bool value)
{
obj.SetValue(ScrollToTopProperty, value);
}
private static void OnScrollToTopPropertyChanged(DependencyObject dpo,
DependencyPropertyChangedEventArgs e)
{
ItemsControl itemsControl = dpo as ItemsControl;
if (itemsControl != null)
{
DependencyPropertyDescriptor dependencyPropertyDescriptor =
DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl));
if (dependencyPropertyDescriptor != null)
{
if ((bool)e.NewValue == true)
{
dependencyPropertyDescriptor.AddValueChanged(itemsControl, ItemsSourceChanged);
}
else
{
dependencyPropertyDescriptor.RemoveValueChanged(itemsControl, ItemsSourceChanged);
}
}
}
}
static void ItemsSourceChanged(object sender, EventArgs e)
{
ItemsControl itemsControl = sender as ItemsControl;
EventHandler eventHandler = null;
eventHandler = new EventHandler(delegate
{
if (itemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
ScrollViewer scrollViewer = GetVisualChild<ScrollViewer>(itemsControl) as ScrollViewer;
scrollViewer.ScrollToTop();
itemsControl.ItemContainerGenerator.StatusChanged -= eventHandler;
}
});
itemsControl.ItemContainerGenerator.StatusChanged += eventHandler;
}
}
And an implementation of GetVisualChild
private T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
Late answer:
A simple solution is to add an event handler for the TargetUpdated event, and set NotifyOnTargetUpdated=True on the ItemsSource binding:
<ListBox x:Name="listBox"
ItemsSource="{Binding MySource, NotifyOnTargetUpdated=True}"
TargetUpdated="ListBox_TargetUpdated"/>
and in the event handler, scroll to the top item:
private void ListBox_TargetUpdated(object sender, DataTransferEventArgs e)
{
if (listBox.Items.Count > 0)
{
listBox.ScrollIntoView(listBox.Items[0]);
}
}
Try this:
if (listBox.Items.Count > 0) {
listBox.ScrollIntoView(listBox.Items[0]);
}
Improved Fredrik Hedblad's answer to work with ObservableCollection:
public static class ItemsControlAttachedProperties
{
#region ScrollToTopOnItemsSourceChange Property
public static readonly DependencyProperty ScrollToTopOnItemsSourceChangeProperty =
DependencyProperty.RegisterAttached(
"ScrollToTopOnItemsSourceChange",
typeof(bool),
typeof(ItemsControlAttachedProperties),
new UIPropertyMetadata(false, OnScrollToTopOnItemsSourceChangePropertyChanged));
public static bool GetScrollToTopOnItemsSourceChange(DependencyObject obj)
{
return (bool) obj.GetValue(ScrollToTopOnItemsSourceChangeProperty);
}
public static void SetScrollToTopOnItemsSourceChange(DependencyObject obj, bool value)
{
obj.SetValue(ScrollToTopOnItemsSourceChangeProperty, value);
}
static void OnScrollToTopOnItemsSourceChangePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var itemsControl = obj as ItemsControl;
if (itemsControl == null)
{
throw new Exception("ScrollToTopOnItemsSourceChange Property must be attached to an ItemsControl based control.");
}
DependencyPropertyDescriptor descriptor =
DependencyPropertyDescriptor.FromProperty(ItemsControl.ItemsSourceProperty, typeof(ItemsControl));
if (descriptor != null)
{
if ((bool) e.NewValue)
{
descriptor.AddValueChanged(itemsControl, ItemsSourceChanged);
}
else
{
descriptor.RemoveValueChanged(itemsControl, ItemsSourceChanged);
}
}
}
static void ItemsSourceChanged(object sender, EventArgs e)
{
var itemsControl = sender as ItemsControl;
DoScrollToTop(itemsControl);
var collection = itemsControl.ItemsSource as INotifyCollectionChanged;
if (collection != null)
{
collection.CollectionChanged += (o, args) => DoScrollToTop(itemsControl);
}
}
static void DoScrollToTop(ItemsControl itemsControl)
{
EventHandler eventHandler = null;
eventHandler =
delegate
{
if (itemsControl.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
{
var scrollViewer = GetVisualChild<ScrollViewer>(itemsControl);
scrollViewer.ScrollToTop();
itemsControl.ItemContainerGenerator.StatusChanged -= eventHandler;
}
};
itemsControl.ItemContainerGenerator.StatusChanged += eventHandler;
}
static T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < numVisuals; i++)
{
var v = (Visual) VisualTreeHelper.GetChild(parent, i);
child = v as T ?? GetVisualChild<T>(v);
if (child != null)
{
break;
}
}
return child;
}
#endregion
}
When you format the control, you select a range of cells as the selection choices which are then listed in the list box. You also select a cell as the link to the selected choices in which a number will be displayed depending on the position of the selection in the list. 1 for first in the list, 2 for second etc. The code is quite simply:-
Range("A1")Select
Selection = 1
Change ("A1") to the cell you have linked
and change the 1 to the position in the list you want selected.
The cell reference being a link works both ways - if you change your selection, the number in the cell changes and if you change the number in the cell, the highlighted selection changes.

raising event in a usercontrol from another usercontrol

How to raise a event in usercontrol from the another usercontrol. i tried to do with delegates, but it doesnt work. How can i do this. am using C#(WPF)
usercontrol1.cs
public partial class UserControl1 : UserControl
{
delegate void myDelegate();
public UserControl1()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
UserControl2 obj = new UserControl2();
myDelegate d = new myDelegate(obj.CallDelegate);
obj.CallDelegate();
}
}
Usercontrol2.cs
public partial class UserControl2 : UserControl
{
public UserControl2()
{
InitializeComponent();
}
public void CallDelegate()
{
this.Width = 50;
this.Height = 50;
MessageBox.Show("Method called ");
}
}
when i use delegate i can go get the messagebox from the method, but the control doesnt resize. do i want to render it again ?? i have tried to assign explicitly, but doesnt work
In general, only the event owner can raise an event. There are exceptions to this (such as with Button.PerformClick in Windows Forms, but they have to be specifically provided by the class in question.
It's possible that WPF routed events may give an alternative here, but you haven't been clear about what kind of events you're talking about. An example of what you're trying to do would be helpful.
that's because in your code you raise an event on a new UserControl2. for your specific example the code of the UserControl1.button1_Click event should be like this:
private void button1_Click(object sender, RoutedEventArgs e)
{
if (this.Parent != null && this.Parent is StackPanel)
{
StackPanel parentControl = this.Parent as StackPanel;
foreach (UIElement child in parentControl.Children)
{
if (child is UserControl2)
((UserControl2)child).CallDelegate();
}
}
}
EDIT:
kay so it seems you want to get all the usercontrol2 within the window1 to be resized. then what you need is to make a recursive function to get the topmost parent, e.g (modded from hardcodet.net/2008/02/find-wpf-parent)
DependencyObject GetHighestParent(DependencyObject child)
{
ContentElement contentElement = child as ContentElement;
if (contentElement != null)
{
DependencyObject parent = ContentOperations.GetParent(contentElement);
if (parent != null) return parent;
FrameworkContentElement fce = contentElement as FrameworkContentElement;
return fce != null ? fce.Parent : null;
}
FrameworkElement frameworkElement = child as FrameworkElement;
if (frameworkElement != null)
{
DependencyObject parent = frameworkElement.Parent;
if (parent != null)
{
return GetHighestParent(parent);
}
else
{
return child;
}
}
DependencyObject visualParent = VisualTreeHelper.GetParent(child);
if (visualParent != null)
return GetHighestParent(visualParent);
else
return child;
}
then you might want to create a method to walkdown all the children like this:
void CallDelegateInAllControl2(DependencyObject parent)
{
int childCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childCount; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if (child is UserControl2)
{
((UserControl2)child).CallDelegate();
}
else
{
CallDelegateInAllControl2(child);
}
}
}
and then you call it within button1_click event
private void button1_Click(object sender, RoutedEventArgs e)
{
DependencyObject parent = GetHighestParent(this);
if(parent!=null)
CallDelegateInAllControl2(parent);
}
note: a walk to get parent and child might be tricky and risky i think and i believe it's a long process so you might just want to re-layout your window1 so it has a StackPanel/Grid with a usercontrol1 element and all usercontrol2 elements within it so you can use the first code i post.

Adding button into a Listview in WinForms

Is there a way to add a button control to a cell in inside a ListView in a WinForms app?
Here is a code of a class ListViewExtender that you can reuse. It's not a derived class of ListView, basically you just declare that a specific column is displayed as buttons instead of text. The button's text is the subItem's text.
It allows big sized list views without problems, does not use p/invoke, and also works with horizontal scrollbars (some code proposed as answers here don't or are quite slow with a great number of items). Note it requires the extended ListView to have FullRowSelect set to true and view type set to Details.
This is a sample code that uses it:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent(); // you need to add a listView named listView1 with the designer
listView1.FullRowSelect = true;
ListViewExtender extender = new ListViewExtender(listView1);
// extend 2nd column
ListViewButtonColumn buttonAction = new ListViewButtonColumn(1);
buttonAction.Click += OnButtonActionClick;
buttonAction.FixedWidth = true;
extender.AddColumn(buttonAction);
for (int i = 0; i < 10000; i++)
{
ListViewItem item = listView1.Items.Add("item" + i);
item.SubItems.Add("button " + i);
}
}
private void OnButtonActionClick(object sender, ListViewColumnMouseEventArgs e)
{
MessageBox.Show(this, #"you clicked " + e.SubItem.Text);
}
}
}
Here is the ListViewExtender code and associated classes:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.VisualStyles;
namespace WindowsFormsApplication1
{
public class ListViewExtender : IDisposable
{
private readonly Dictionary<int, ListViewColumn> _columns = new Dictionary<int, ListViewColumn>();
public ListViewExtender(ListView listView)
{
if (listView == null)
throw new ArgumentNullException("listView");
if (listView.View != View.Details)
throw new ArgumentException(null, "listView");
ListView = listView;
ListView.OwnerDraw = true;
ListView.DrawItem += OnDrawItem;
ListView.DrawSubItem += OnDrawSubItem;
ListView.DrawColumnHeader += OnDrawColumnHeader;
ListView.MouseMove += OnMouseMove;
ListView.MouseClick += OnMouseClick;
Font = new Font(ListView.Font.FontFamily, ListView.Font.Size - 2);
}
public virtual Font Font { get; private set; }
public ListView ListView { get; private set; }
protected virtual void OnMouseClick(object sender, MouseEventArgs e)
{
ListViewItem item;
ListViewItem.ListViewSubItem sub;
ListViewColumn column = GetColumnAt(e.X, e.Y, out item, out sub);
if (column != null)
{
column.MouseClick(e, item, sub);
}
}
public ListViewColumn GetColumnAt(int x, int y, out ListViewItem item, out ListViewItem.ListViewSubItem subItem)
{
subItem = null;
item = ListView.GetItemAt(x, y);
if (item == null)
return null;
subItem = item.GetSubItemAt(x, y);
if (subItem == null)
return null;
for (int i = 0; i < item.SubItems.Count; i++)
{
if (item.SubItems[i] == subItem)
return GetColumn(i);
}
return null;
}
protected virtual void OnMouseMove(object sender, MouseEventArgs e)
{
ListViewItem item;
ListViewItem.ListViewSubItem sub;
ListViewColumn column = GetColumnAt(e.X, e.Y, out item, out sub);
if (column != null)
{
column.Invalidate(item, sub);
return;
}
if (item != null)
{
ListView.Invalidate(item.Bounds);
}
}
protected virtual void OnDrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
{
e.DrawDefault = true;
}
protected virtual void OnDrawSubItem(object sender, DrawListViewSubItemEventArgs e)
{
ListViewColumn column = GetColumn(e.ColumnIndex);
if (column == null)
{
e.DrawDefault = true;
return;
}
column.Draw(e);
}
protected virtual void OnDrawItem(object sender, DrawListViewItemEventArgs e)
{
// do nothing
}
public void AddColumn(ListViewColumn column)
{
if (column == null)
throw new ArgumentNullException("column");
column.Extender = this;
_columns[column.ColumnIndex] = column;
}
public ListViewColumn GetColumn(int index)
{
ListViewColumn column;
return _columns.TryGetValue(index, out column) ? column : null;
}
public IEnumerable<ListViewColumn> Columns
{
get
{
return _columns.Values;
}
}
public virtual void Dispose()
{
if (Font != null)
{
Font.Dispose();
Font = null;
}
}
}
public abstract class ListViewColumn
{
public event EventHandler<ListViewColumnMouseEventArgs> Click;
protected ListViewColumn(int columnIndex)
{
if (columnIndex < 0)
throw new ArgumentException(null, "columnIndex");
ColumnIndex = columnIndex;
}
public virtual ListViewExtender Extender { get; protected internal set; }
public int ColumnIndex { get; private set; }
public virtual Font Font
{
get
{
return Extender == null ? null : Extender.Font;
}
}
public ListView ListView
{
get
{
return Extender == null ? null : Extender.ListView;
}
}
public abstract void Draw(DrawListViewSubItemEventArgs e);
public virtual void MouseClick(MouseEventArgs e, ListViewItem item, ListViewItem.ListViewSubItem subItem)
{
if (Click != null)
{
Click(this, new ListViewColumnMouseEventArgs(e, item, subItem));
}
}
public virtual void Invalidate(ListViewItem item, ListViewItem.ListViewSubItem subItem)
{
if (Extender != null)
{
Extender.ListView.Invalidate(subItem.Bounds);
}
}
}
public class ListViewColumnMouseEventArgs : MouseEventArgs
{
public ListViewColumnMouseEventArgs(MouseEventArgs e, ListViewItem item, ListViewItem.ListViewSubItem subItem)
: base(e.Button, e.Clicks, e.X, e.Y, e.Delta)
{
Item = item;
SubItem = subItem;
}
public ListViewItem Item { get; private set; }
public ListViewItem.ListViewSubItem SubItem { get; private set; }
}
public class ListViewButtonColumn : ListViewColumn
{
private Rectangle _hot = Rectangle.Empty;
public ListViewButtonColumn(int columnIndex)
: base(columnIndex)
{
}
public bool FixedWidth { get; set; }
public bool DrawIfEmpty { get; set; }
public override ListViewExtender Extender
{
get
{
return base.Extender;
}
protected internal set
{
base.Extender = value;
if (FixedWidth)
{
base.Extender.ListView.ColumnWidthChanging += OnColumnWidthChanging;
}
}
}
protected virtual void OnColumnWidthChanging(object sender, ColumnWidthChangingEventArgs e)
{
if (e.ColumnIndex == ColumnIndex)
{
e.Cancel = true;
e.NewWidth = ListView.Columns[e.ColumnIndex].Width;
}
}
public override void Draw(DrawListViewSubItemEventArgs e)
{
if (_hot != Rectangle.Empty)
{
if (_hot != e.Bounds)
{
ListView.Invalidate(_hot);
_hot = Rectangle.Empty;
}
}
if ((!DrawIfEmpty) && (string.IsNullOrEmpty(e.SubItem.Text)))
return;
Point mouse = e.Item.ListView.PointToClient(Control.MousePosition);
if ((ListView.GetItemAt(mouse.X, mouse.Y) == e.Item) && (e.Item.GetSubItemAt(mouse.X, mouse.Y) == e.SubItem))
{
ButtonRenderer.DrawButton(e.Graphics, e.Bounds, e.SubItem.Text, Font, true, PushButtonState.Hot);
_hot = e.Bounds;
}
else
{
ButtonRenderer.DrawButton(e.Graphics, e.Bounds, e.SubItem.Text, Font, false, PushButtonState.Default);
}
}
}
}
The ListView itself (or ListViewItem) does not function as a container of any kind so no way to add controls directly, however it is doable. I have used this extended ListView with a lot of success: Embedding Controls in a ListView.
This is the BEST custom listview control for WinForms.
ObjectListView
To make the extender of Simon Mourier working is missing the following line:
extender.AddColumn(buttonAction);
This is, it should look like:
ListViewExtender extender = new ListViewExtender(listSummary);
ListViewButtonColumn buttonAction = new ListViewButtonColumn(2);
buttonAction.Click += OnButtonActionClick;
buttonAction.FixedWidth = true;
extender.AddColumn(buttonAction);
Maybe this could be of interest?
http://www.codeproject.com/KB/list/extendedlistviews.aspx
No, a standard Windows Forms ListView doesn't support embedded controls. You could try to build your own custom control, or you could use something like http://www.codeproject.com/KB/list/EXListView.aspx.
No and yes, ListView itself does not support such functionality, but you can create a button on top of it, so that it appears to the user as integral part of the listview. (I suppose this is what the ExtendedListView mentioned above does too).
Maybe it worths mentioning, the list view control might be designed in WPF as an usercontrol/custom control with buttons in its ListViewItems, and then use this control in the WinForms application, in an ElementHost control.
I accidentally come across a discussion before, hope this help: http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/ee232cc4-68c5-4ed3-9ea7-d4d999956504/
You could use a GlacialList. It allow you to put ANY control inside a list cell and it's simple to use. You will just need to join a GlacialList.dll document to the reference part of your Solution. If you click the link it will show you how it works and how to use it and download it.
If you have a System.IO.FileNotFoundException on the InitializeComponent() just download source code from the above link, compile and use this .dll (inside bin/Debug subfolder) to your project .
Here is an example of what it looks like:
This looks like the simplest answer I have come across... just added an ItemCommand to the ListView.
See this link: handle-the-button-click-event-from-an-asp-net-listview-control

Resources