Getting a cell container from WPF ListView - c#

I have a list view on a component which is bound to an observable collection of type BreakdownViewRow.
Each cell contains a component which operating in EDIT and DISPLAY modes.
I need to find a way to get to programatially change the mode on the cell content, but trying to iterate over the items in the list view returns objects of type BreakdownViewRow.
Is there a way I can access the cell containing each component, rather than getting back items as they exist in my observable collection?
The code for my component (the ContainerSelected event is bound in the XAML, and fires correctly passing the EditableControlContainer object as the sender):
public partial class ArrearsBreakdownView : UserControl
{
private ObservableCollection<BreakdownViewRow> rows;
public ObservableCollection<BreakdownViewRow> Rows {
get { return rows; }
set { rows = value; }
}
public BreakdownView() {
InitializeComponent();
rows = new ObservableCollection<BreakdownViewRow>();
Populate();
this.DataContext = this;
}
public void Populate() {
rows.Add(new BreakdownViewRow(2015, 1000.50M, "TE1 \t Test Code 1", "This is a test row"));
rows.Add(new BreakdownViewRow(2014, 100M, "TE1 \t Test Code 1", "Another row"));
rows.Add(new BreakdownViewRow(2013, 150M, "TE1 \t Test Code 1", "This is a test row"));
}
public void ContainerSelected(object sender, RoutedEventArgs e) {
EditableControlContainer ecc = (EditableControlContainer)sender;
foreach (ListViewItem item in grdArrears.Items) {
//Here, I get a BreakdownViewRow object, when what I need is the EditableControlContainer object,
//or the cell container
}
int stop = 1;
}
}

You can try using the ItemContainerGenerator. It'll return the container of the row, but you can then use the TreeViewHelper to get the ContentPresenters of each cell, and then look for your control inside each ContentPresenter's Template:
public void ContainerSelected(object sender, RoutedEventArgs e) {
EditableControlContainer ecc = (EditableControlContainer)sender;
ItemContainerGenerator generator = grdArrears.ItemContainerGenerator;
foreach (var item in grdArrears.Items) {
var itemContainer = generator.ContainerFromItem(item);
foreach (var presenter in GetChildrenOfType<ContentPresenter>(itemContainer))
{
var myControl = presenter.ContentTemplate.FindName("MyControl", presenter);
// Do stuff with your cell control
}
}
}
private IEnumerable<T> GetChildrenOfType<T>(DependencyObject parent) where T : DependencyObject
{
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T)
yield return (T)child;
var childrenOfType = GetChildrenOfType<T>(child);
foreach (var childOfType in childrenOfType)
yield return childOfType;
}
}

Related

How to disable one ComboBox in a DataGridViewComboboxColumn where a nested property is presented?

I display list of objects in dataGridView. The list is like:
List<IAction> actions = new List<IAction>
I displayed nested property (not defined in the IAction interface) on DataGridViewComboBoxColumn() using karlipoppins answer here (reflection) Is it possible to bind complex type properties to a datagrid?
but I have a problem with making the combobox disabled when where is no Area property in one kind of object
Hope you will help me ;)
On the list there are two types of objects:
public class MoveAction : IAction
{
public string Name { get; set; }
public bool Active { get; set; } = true;
}
public class ClickAction : IAction
{
public string Name { get; set; }
public bool Active { get; set; } = true;
public Area Area { get; set; } ////////// Additional Property
}
this additional property object looks like:
public class Area
{
public string Name { get; set; }
...
}
The dataGridView column is defined as follows:
DataGridViewComboBoxColumn CreateComboBoxWithArea()
{
DataGridViewComboBoxColumn combo = new DataGridViewComboBoxColumn();
combo.DataSource = areas.Select(t => t.Name).ToArray();
combo.DataPropertyName = "Area.Name";
combo.Name = "Area";
return combo;
}
I've found my own solution:
I have an access to the whole grid after casting the sender. After that I can do what I want with this one comboBox (hide the button, set readonly... ). That's because the fact the combobox has no property bool Enable ;/
Thanks for help jaredbaszler!
private void dataGridView1_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
DataGridView grid = (DataGridView)sender;
DataGridViewRow row = grid.Rows[e.RowIndex];
DataGridViewColumn col = grid.Columns[e.ColumnIndex];
if (row.DataBoundItem != null && col.DataPropertyName.Contains("."))
{
string[] props = col.DataPropertyName.Split('.');
PropertyInfo propInfo = row.DataBoundItem.GetType().GetProperty(props[0]);
if(propInfo != null)
{
object val = propInfo.GetValue(row.DataBoundItem, null);
for (int i = 1; i < props.Length; i++)
{
propInfo = val.GetType().GetProperty(props[i]);
val = propInfo.GetValue(val, null);
}
e.Value = val;
}
else
{
DataGridViewCell cell = grid.Rows[e.RowIndex].Cells[e.ColumnIndex];
DataGridViewComboBoxCell chkCell = cell as DataGridViewComboBoxCell;
chkCell.DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing;
cell.ReadOnly = true;
}
}
}
You can tie into the cell's BeginEdit event and cancel the editing of the combo at that point. Like:
dataGrid.CellBeginEdit += dataGrid_CellBeginEdit;
void dgSamples_CellBeginEdit(object sender, GridViewCellCancelEventArgs e)
{
var areaObject = e.Row.Cells["Area"].Value AS Area; // .Value will be the current selected item in the cell's combo.
if (areaObject.Area == null)
{
e.Cancel = true;
}
}
This won't show the combo as 'disabled' but it will prevent the user from dropping down the combo and changing the value. Essentially make the cell read only.
Lastly, I'm not positive what type your areas list object is when you define the data source for the combo box. So I'm guessing at the type when I cast the .Value property to Areas. You may need to change that.

Adding a control to the main container of unknown object

void Visualize(object CoreObj, object ParentControl)
{
if(CoreObj is typeA)
{
object control1 = new MyControl1(CoreObj);
ParentControl.FirstChild.Children.Add(control1);
foreach (object obj in CoreObj.Children)
{
Visualize(obj, control1);
}
}
else if (CoreObj is typeB)
{
object control2 = new MyControl2(CoreObj);
ParentControl.FirstChild.Children.Add(control2);
foreach (object obj in CoreObj.Children)
{
Visualize(obj, control2);
}
}
}
Where FirstChild is always container, no matter StackPanel, Grid or Canvas, or whatever.
How I get the first child, and the harder part, how to do Children.Add() on object?
I can require something else in case "Children" is inherited from somewhere in all wpf containers, but I can't find out which ancestor/interface contains "Children". Or I can use Reflection probably..
How to do this?
Here's what I came with, finally
interface IContain
{
Panel GetMain(); //return main container
}
// ...
void Visualize(object CoreObj, Panel ParentControl)
{
UIElement control = new UIElement();
if (CoreObj is File) { control = new NameSpacer(); } //new NameSpacer(obj);
else if (CoreObj is Namespace) { control = new NameSpacer(); }
else if(CoreObj is Using) { control = new NameSpacer(); }
if (control.GetType() == typeof(UIElement)) return;
ParentControl.Children.Add(control);
FieldInfo finf = CoreObj.GetType().GetField("Children"); if (finf == null) return;
var val = finf.GetValue(CoreObj); if (val.GetType() != typeof(IEnumerable<object>)) return;
if (control is IContain == false) return;
Panel container = ((IContain)control).GetMain();
foreach (object o in val as IEnumerable<object>)
{
Visualize(o, container);
}
}
You can use VisualTreeHelper class to get the first child.
Children property is defined in abstract class Panel.
var firstChild = parentControl.Descendants().OfType<Panel>().First();
firstChild.Children.Add(control1);
The descendants method leverages VisualTreeHelper in order to get all descendants and you have to define it as extension method:
public static IEnumerable<DependencyObject> Descendants(this DependencyObject element)
{
int childrenCount = VisualTreeHelper.GetChildrenCount(element);
for (int i = 0; i < childrenCount; i++)
{
var visualChild = VisualTreeHelper.GetChild(element, i);
yield return visualChild;
foreach (var visualChildren in Descendants(visualChild))
{
yield return visualChildren;
}
}
}
StackPanel, Grid and Canvas, all derive from the Panel class. This is also the class that contains the Children property.
if you know your argument is a Panel, you also have access to Children.First() and Children.Add(..)

Check more than one checkbox upon clicking a button c#

I have for example:
Checkbox1.IsChecked = true;
Checkbox2.IsChecked = true;
Checkbox3.IsChecked = true;
I have this 32 times. Is there a way to have concat string? For example:
i = 1
while i < 32:
("Checkbox"+ (i)).IsChecked = true;
i++
Thanks
While you cannot do exactly what you intend, you can check or uncheck all checkboxes that are in a given container. For example, let's say you have a Panel that contains a number of checkboxes, called pnlChecks. You could do something like
foreach (var chkBox in pnlChecks.Controls.OfType<CheckBox>())
{
chkBox.IsChecked = true;
}
There are multiple methods to achieve this.
Add all of them to a generic List<> and iterate through them like the for you mentioned.
Use reflection and get the checkbox controls and set their value.
Sample WinForms Code
private List<CheckBox> checkboxes = new List<CheckBox>();
public Form1()
{
InitializeComponent();
FillCheckboxes();
}
private void CheckAll()
{
foreach (var chk in checkboxes)
{
chk.Checked = true;
}
}
private void FillCheckboxes()
{
foreach (Control c in this.Controls)
{
if (c is CheckBox)
{
checkboxes.Add(c as CheckBox);
}
}
}
private void button1_Click(object sender, EventArgs e)
{
CheckAll();
}
Sample WPF Code
private List<CheckBox> checkboxes = new List<CheckBox>();
public Window1()
{
InitializeComponent();
checkboxes = FindVisualChildren<CheckBox>(main).ToList();
CheckAll();
}
private void CheckAll()
{
foreach (var chk in checkboxes)
{
chk.IsChecked = true;
}
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
Important Note
For WPF the suggested method is to use data binding instead of iterating through controls and manually checking/unchecking them. Just bind the IsChecked property to the desired value and change it. You can find more info regarding this on numerous articles on the Internet.

Bind Data to Windows Form TabControl

I'm attempting my first Windows Form project, having been entirely web based previously and experiencing some issues. I want to bind a list of objects to a TabControl and have this create the Tabs and then have a databound value accessible from the click event of each tab.
The Object I'm wanting to bind is
public class TreeNodeItem
{
private NTree<string> node;
public TreeNodeItem(NTree<string> node)
{
this.node = node;
}
public string Value
{
get { return this.node.data; }
}
}
The NTree node represents a node in an object that models data in a tree structure. I want to create a tab for each object in the list with the Value property being bound to the Tab Text property. Other posts mention binding to the ItemsSource property of the control, but Visual Studio is not giving me this property.
Any help will be greatly appreciated.
Cheers
Stewart
Okay, I was unaware of that the binding was a must. Although I have never seen something like this being done in a Windows Forms Application, I've decided to create a class that does this for us.
It uses the ObservableCollection<T> to keep track whether an object / property has been changed inside its list.
public class ObservableList<T> : ObservableCollection<T>
{
public ObservableList() : base()
{
CollectionChanged += new NotifyCollectionChangedEventHandler(nObservableCollection_CollectionChanged);
}
public event PropertyChangedEventHandler OnPropertyChanged;
void nObservableCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (OnPropertyChanged != null)
{
OnPropertyChanged(new object[] { e.OldItems, e.NewItems }, null); // Call method to let it change the tabpages
}
}
}
Now, we have to create a helper class that helps us keeping track:
public class TabControlBind
{
public TabControlBind(TabControl tabControl)
{
// Create a new TabPageCollection and bind it to our tabcontrol
this._tabPages = new TabControl.TabPageCollection(tabControl);
}
// Fields
private ObservableList<TreeNodeItem> _treeNodeItems;
private TabControl.TabPageCollection _tabPages;
// Properties
public ObservableList<TreeNodeItem> TreeNodeItems
{
get { return _treeNodeItems; }
set
{
if (_treeNodeItems != value)
{
_treeNodeItems = value;
_treeNodeItems.OnPropertyChanged += OnPropretyChanged;
OnPropretyChanged(null, null);
}
}
}
public TabControl.TabPageCollection TabPages
{
get
{
return this._tabPages;
}
}
// Events
private void OnPropretyChanged(object sender, PropertyChangedEventArgs e)
{
if (sender == null) // If list got set
{
// Remove existing tabpages
this._tabPages.Clear();
// Loop through all items inside the ObservableList object and add them to the Tabpage
foreach (TreeNodeItem _treeNodeItem in this._treeNodeItems)
{
TabPage tabPage = new TabPage() { Text = _treeNodeItem.Value, Tag = _treeNodeItems };
this._tabPages.Add(tabPage);
}
}
else if (sender is object[]) // If only one (or multiple) objects have been changed
{
// Get OldItems and NewItems
object[] changedItems = (object[])sender;
// Remove OldItems
if (changedItems[0] != null)
{
foreach (dynamic oldItems in (IList)changedItems[0])
{
foreach (TabPage tab in this._tabPages)
{
if (tab.Text == oldItems.Value)
{
this._tabPages.Remove(tab);
break;
}
}
}
}
// Add OldItems
if (changedItems[1] != null)
{
foreach (dynamic newItems in (IList)changedItems[1])
{
TabPage tabPage = new TabPage() { Text = newItems.Value, Tag = newItems };
this._tabPages.Add(tabPage);
}
}
}
}
}
This is a sample on how to use it:
TabControlBind tabControlBinder;
ObservableList<TreeNodeItem> treeNodeItems;
private void btnAdd_Click(object sender, EventArgs e)
{
// This will automatically update the TabControl
treeNodeItems.Add(new TreeNodeItem(new NTree<string>() { data = "Test3" }));
}
private void frmMain_Load(object sender, EventArgs e)
{
// Create a new list object an add items to it
treeNodeItems = new ObservableList<TreeNodeItem>();
treeNodeItems.Add(new TreeNodeItem(new NTree<string>() { data = "Test" }));
treeNodeItems.Add(new TreeNodeItem(new NTree<string>() { data = "Test2" }));
// Create a new instance of the TabControlBind class, set it to our TabControl
tabControlBinder = new TabControlBind(tabControl);
tabControlBinder.TreeNodeItems = treeNodeItems;
}

C# : How to cast an object to ItemSource data type?

I am trying to develop a custom User control. In my User Control I use two controls a List Box and a Text Box. Text Box is use for filtering items in List Box. To doing this I am facing a problem in my filter method. In my filter method I need to cast object to ItemSource type. But I don`t understand how can I cast it. Here is my Code which I try:
public static readonly DependencyProperty ItemSourceProperty = DependencyProperty.Register("ItemSourrce", typeof(IEnumerable), typeof(SSSearchListBox), new PropertyMetadata(new PropertyChangedCallback(OnItemsSourcePropertyChanged)));
private static void OnItemsSourcePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var control = sender as SSSearchListBox;
if (control != null)
control.OnItemsSourceChanged((IEnumerable)e.OldValue, (IEnumerable)e.NewValue);
}
private void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue)
{
// Remove handler for oldValue.CollectionChanged
var oldValueINotifyCollectionChanged = newValue as INotifyCollectionChanged;
if (null != oldValueINotifyCollectionChanged)
{
oldValueINotifyCollectionChanged.CollectionChanged -= new NotifyCollectionChangedEventHandler(newValueINotifyCollectionChanged_CollectionChanged);
}
// Add handler for newValue.CollectionChanged (if possible)
var newValueINotifyCollectionChanged = newValue as INotifyCollectionChanged;
if (null != newValueINotifyCollectionChanged)
{
newValueINotifyCollectionChanged.CollectionChanged += new NotifyCollectionChangedEventHandler(newValueINotifyCollectionChanged_CollectionChanged);
}
}
void newValueINotifyCollectionChanged_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
//Do your stuff here.
}
public IEnumerable ItemSourrce
{
get { return (IEnumerable)this.GetValue(ItemSourceProperty); }
set { this.SetValue(ItemSourceProperty, value); }
}
public void TextFiltering(ICollectionView filteredview, DevExpress.Xpf.Editors.TextEdit textBox)
{
string filterText = "";
filteredview.Filter = delegate(object obj)
{
if (String.IsNullOrEmpty(filterText))
{
return true;
}
string str = obj as string; // Problem is here.
// I need to cast obj to my ItemSourrce Data Type.
if (String.IsNullOrEmpty(obj.ToString()))
{
return true;
}
int index = str.IndexOf(filterText, 0, StringComparison.InvariantCultureIgnoreCase);
return index > -1;
};
textBox.EditValueChanging += delegate
{
filterText = textBox.Text;
filteredview.Refresh();
};
}
private void textEdit1_GotFocus(object sender, RoutedEventArgs e)
{
ICollectionView view = CollectionViewSource.GetDefaultView(ItemSourrce);
TextFiltering(view, textEdit1);
}
calling this Use Control :
List<testClass> myList = new List<testClass>();
public void testMethod()
{
for (int i = 0; i < 20; i++)
{
myList.Add(new testClass { testData=i.ToString()});
}
myTestControl.ItemSourrce = myList;
}
public class testClass
{
public string testData { get; set; }
}
thank`s
well everything what you want to assing to ItemSource (or ItemsSource for naming consistence) has to implement the IEnumerable interface. Thist means basicaly erverything you can iterate through a foreach (to put it simply). So every List, array, collections, Dictionaries and so on.
Normaly you can't iterate throu a normal out of the box string!
So if your Object is realy a String (check it with a breakpoint on debugging) you have to split it somehow.
ps: by the way this code
if (String.IsNullOrEmpty(obj.ToString()))
{
return true;
}
never (except some rare unmeaningfull example where you explicitly return an empty string for ToStirng()) returns true because obj.ToString() is never null or Empty. In the standard implementation of the type Object (which every .net Type is based on) it returns the Namespace and the name of the type.
If I understood well your objects are of type testClass and not of string.
So you code should be where you try to do the casting.
string str = string.Empty;
testClass myObject = obj as testClass;
if (myObject == null)
return true;
else
str = myObject.testData;
if (string.IsNullOrEmpty(str))
return true;
return str.IndexOf(filterText, 0, StringComparison.InvariantCultureIgnoreCase) > -1;

Categories

Resources