UnifomGrid has loaded Data, I want to Hide the first row?
How can I implement that?
I've never worked with the UniformGrid so there might be a better solution for this but as far as I can tell the UniformGrid doesn't hold much information except how many rows and columns it currently have so therefore this is the only solution that I can think of.
private List<UIElement> GetElementsAtRow(int rowNumber)
{
List<UIElement> elementsAtRow = new List<UIElement>();
for (int i = 0; i < uniformGrid.Columns; i++)
{
if (i < uniformGrid.Children.Count)
{
elementsAtRow.Add(uniformGrid.Children[i] as UIElement);
}
}
return elementsAtRow;
}
private void HideFirstRow()
{
List<UIElement> elementsAtRow = GetElementsAtRow(0);
foreach (UIElement element in elementsAtRow)
{
// Or Hidden if you want row to remain but with no Visible children.
element.Visibility = Visibility.Collapsed;
}
}
private void ShowFirstRow()
{
List<UIElement> elementsAtRow = GetElementsAtRow(0);
foreach (UIElement element in elementsAtRow)
{
element.Visibility = Visibility.Visible;
}
}
Related
I am using a DataGrid in WPF and want it to shrink to only fit the width of its columns. It does this nicely for the initial rendering. When I resize a column to make it wider the grid grows as well. But if I resize the column to make it narrower again I get white space on the right side of my column (and I can see that the column header grey area is extended beyond the columns.
I would like to have the data grid shrink its width with the columns so I don't get the white space on the right. I have tried to debug the code and as far as I can see the problem is in the DataGridCellsPanel, but I can't see anyplace to fix the width measurement.
Any help would be appreciated.
I had that problem to a while back and I was getting so annoyed by it that I made an ugly fix for it. It's not pretty, but it gets the job done. First, this is only a problem when the Horizontal ScrollBar is invisible so we're gonna need a reference to it. This code will have to be run once all DataGridColumns have been loaded (in my case, all in Xaml, so the Loaded event) and it doesn't take adding/removing of DataGridColumns into consideration but that's an easy fix.
<DataGrid Name="c_dataGrid"
Loaded="c_dataGrid_Loaded"
...>
<DataGrid.Columns>
<DataGridTextColumn ..."/>
<DataGridTextColumn ..."/>
<!-- ... -->
Then in the Loaded EventHandler we get the DataGrid ScrollViewer and add a listener for changes in the ActualWidthProperty of every DataGridColumn in the DataGrid.
private ScrollViewer m_dataGridScrollViewer = null;
private void c_dataGrid_Loaded(object sender, RoutedEventArgs e)
{
m_dataGridScrollViewer = GetVisualChild<ScrollViewer>(c_dataGrid);
DependencyPropertyDescriptor dependencyPropertyDescriptor =
DependencyPropertyDescriptor.FromProperty(DataGridColumn.ActualWidthProperty, typeof(DataGridColumn));
if (dependencyPropertyDescriptor != null)
{
foreach (DataGridColumn column in c_dataGrid.Columns)
{
dependencyPropertyDescriptor.AddValueChanged(column, DataGridColumn_ActualWidthChanged);
}
}
}
And then we compute the size of the DataGrid from the size of all DataGridColumns and add a constant of 8.0 (which is the difference normally).
private void DataGridColumn_ActualWidthChanged(object sender, EventArgs e)
{
if (m_dataGridScrollViewer != null)
{
if (m_dataGridScrollViewer.ComputedHorizontalScrollBarVisibility != Visibility.Visible)
{
double dataGridWidth = 8.0;
foreach (DataGridColumn column in c_dataGrid.Columns)
{
dataGridWidth += column.ActualWidth;
}
c_dataGrid.Width = dataGridWidth;
}
else
{
c_dataGrid.Width = double.NaN;
}
}
}
If you come up with a better way of doing this then let me know :)
public static T GetVisualChild<T>(object parent) where T : Visual
{
DependencyObject dependencyObject = parent as DependencyObject;
return InternalGetVisualChild<T>(dependencyObject);
}
private static T InternalGetVisualChild<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;
}
That's a nice solution. I have tweaked it slightly so that it sets the MaxWidth property instead. This solves the problem of the grid expanding beyond the constraints of the visual parent. I also converted it into a behavior instead in order to encapsulate it better.
This is what I ended up with.
public class UpdateWidthOnColumnResizedBehavior : Behavior<DataGrid>
{
private static readonly DependencyPropertyDescriptor Descriptor;
static UpdateWidthOnColumnResizedBehavior()
{
Descriptor = DependencyPropertyDescriptor.FromProperty(DataGridColumn.ActualWidthProperty, typeof(DataGridColumn));
}
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Columns.CollectionChanged += OnColumnsCollectionChanged;
foreach (var column in AssociatedObject.Columns)
{
AddListener(column);
}
}
void OnColumnsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
foreach (var column in e.NewItems.OfType<DataGridColumn>())
{
AddListener(column);
}
break;
case NotifyCollectionChangedAction.Remove:
foreach (var column in e.OldItems.OfType<DataGridColumn>())
{
RemoveListener(column);
}
break;
case NotifyCollectionChangedAction.Replace:
foreach (var column in e.NewItems.OfType<DataGridColumn>())
{
AddListener(column);
}
foreach (var column in e.OldItems.OfType<DataGridColumn>())
{
RemoveListener(column);
}
break;
}
}
protected override void OnDetaching()
{
base.OnDetaching();
foreach (var column in AssociatedObject.Columns)
{
RemoveListener(column);
}
}
private void AddListener(DataGridColumn column)
{
Descriptor.AddValueChanged(column, ResizeGrid);
}
private void RemoveListener(DataGridColumn column)
{
Descriptor.RemoveValueChanged(column, ResizeGrid);
}
private void ResizeGrid(object sender, EventArgs e)
{
var columnsWidth = AssociatedObject.Columns.Sum(c => c.ActualWidth);
AssociatedObject.MaxWidth = columnsWidth + 2;
AssociatedObject.InvalidateMeasure();
}
}
I still have some things to iron out about width coordination of two grids, but it looks to work for one.
there is seems to be a slight problem with both of your approaches. When I drag the left most column to the right, the whole grid is getting resized/ rolled inside (unfortunatly I don't have enough reputation to post the image).
So I have modified jjrdk ResizeGrid function, so it calculate the last column width and extends it all the way to the left. The grid HorizontalAlignment and HorizontalContentAlignment must be set to
HorizontalAlignment.Stretch.
void ResizeGrid(object sender, EventArgs e)
{
var scroll = ExTreeHelper.FindVisualChild<ScrollViewer>(AssociatedObject);
if (scroll != null && null != AssociatedObject.Columns && AssociatedObject.Columns.Count > 0)
{
var lastColumn = AssociatedObject.Columns.Last();
double dataGridWidth = AssociatedObject.Columns.Sum(c => c.ActualWidth) + 2.0;
if (scroll.ComputedHorizontalScrollBarVisibility != Visibility.Visible)
{
RemoveListener(lastColumn);
AssociatedObject.Columns.Last().Width =
AssociatedObject.Columns.Last().Width.DisplayValue + scroll.ViewportWidth - dataGridWidth;
AssociatedObject.Width = dataGridWidth + scroll.ViewportWidth - dataGridWidth;
AddListener(lastColumn);
}
else
{
AssociatedObject.HorizontalAlignment = HorizontalAlignment.Stretch;
AssociatedObject.HorizontalContentAlignment = HorizontalAlignment.Stretch;
AssociatedObject.Width = double.NaN;
}
} }
The only issue I have, is that the scroll bar is always there, even if all the columns has been fit.
There is still another issue, when all the columns are collapsed to the left, it starts flickering.
Is there anything that can be done, to really get rid of this white space?
Leon
My code is as follows:
void mainDataContextObj_CutSelectedColumnEvent(string columnId)
{
IList<DataGridColumn> columns = dg.Columns;
for(int i=2; i<dg.Columns.Count; i++)
{
DataGridColumnHeader headerObj = dg.Columns[i].Header as DataGridColumnHeader;
//This always returns headerObj as null!!!
}
}
I need DataGridColumnHeader from the column. Where am I going wrong?
The Header object of the DataGridColumn is actually the visible header of that column, whatever you set it to be. DataGridColumn is not part of the Visual Tree so there is not direct way to access the DataGridColumnHeader for it (we can't even be sure it exists yet). But you can do something like this to try and access it
DataGridColumnHeader headerObj = GetColumnHeaderFromColumn(column);
private DataGridColumnHeader GetColumnHeaderFromColumn(DataGridColumn column)
{
// dataGrid is the name of your DataGrid. In this case Name="dataGrid"
List<DataGridColumnHeader> columnHeaders = GetVisualChildCollection<DataGridColumnHeader>(dataGrid);
foreach (DataGridColumnHeader columnHeader in columnHeaders)
{
if (columnHeader.Column == column)
{
return columnHeader;
}
}
return null;
}
public List<T> GetVisualChildCollection<T>(object parent) where T : Visual
{
List<T> visualCollection = new List<T>();
GetVisualChildCollection(parent as DependencyObject, visualCollection);
return visualCollection;
}
private void GetVisualChildCollection<T>(DependencyObject parent, List<T> visualCollection) where T : Visual
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parent, i);
if (child is T)
{
visualCollection.Add(child as T);
}
else if (child != null)
{
GetVisualChildCollection(child, visualCollection);
}
}
}
While Fredrik's answer provides a refactored approach with additional method that could potentially be reused in other parts of the code, I preferred to consolidate his methods in to one single method. There may also be some small performance gain because it can end the search as soon as it finds the header and it does not need to continue to search through all the children in the visual tree (this is most likely negligible for most cases).
private DataGridColumnHeader GetHeader(DataGridColumn column, DependencyObject reference)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(reference); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(reference, i);
DataGridColumnHeader colHeader = child as DataGridColumnHeader;
if ((colHeader != null) && (colHeader.Column == column))
{
return colHeader;
}
colHeader = GetHeader(column, child);
if (colHeader != null)
{
return colHeader;
}
}
return null;
}
And it is used like so:
DataGridColumnHeader colHeader = GetHeader(column, myDataGrid);
if (colHeader == null) { /* Not found */ }
I have a Silverlight 3 application, and it has radiobuttons grouped using the GroupName property. What I would like to do in the code is retrieve all the radiobuttons that are part of a specified group. Is there an easy way to do this, or would I need to iterate over all the controls?
Thanks.
Borrowing (yet again) my VisualTreeEnumeration from this answer (I really really need to blog):-
public static class VisualTreeEnumeration
{
public static IEnumerable<DependencyObject> Descendents(this DependencyObject root)
{
int count = VisualTreeHelper.GetChildrenCount(root);
for (int i=0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(root, i);
yield return child;
foreach (var descendent in Descendents(child))
yield return descendent;
}
}
}
Place this in a file in either your main namespace or in a utility namespace that you place a using for in your code.
Now you can use LINQ to get all sorts of useful lists. In your case:-
List<RadioButton> group = this.Descendents()
.OfType<RadioButton>()
.Where(r => r.GroupName == "MyGroupName")
.ToList();
This might help:
Essentially walk through the controls looking for radiobuttons in the required group.
This will also look through any children panels.
private List<FrameworkElement> FindBindings(DependencyObject visual, string group)
{
var results = new List<FrameworkElement>();
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
{
var childVisual = VisualTreeHelper.GetChild(visual, i);
var childRadioButton = childVisual as RadioButton;
if (childRadioButton != null)
{
if (childRadioButton.GroupName == group)
{
results.Add(childRadioButton);
}
}
else
{
if (childVisual is Panel)
{
results.AddRange(FindBindings(childVisual, group));
}
}
}
return results;
}
I have several Silverlight controls on a page and want query all the controls that are of type TextBox and have that working.
Now the Silverlight form I'm working on could have more TextBox controls added. So when I test to see if a TextBox control has a value, I could do:
if (this.TextBox.Control.value.Text() != String.Empty)
{
// do whatever
}
but I'd rather have if flexible that I can use this on ANY Silverlight form regardless of the number of TextBox controls I have.
Any ideas on how I would go about doing that?
I have already faced this issue and notify it here : http://megasnippets.com/en/source-codes/silverlight/Get_all_child_controls_recursively_in_Silverlight
Here you have a generic method to find recursively in the VisualTree all TextBoxes:
IEnumerable<DependencyObject> GetChildrenRecursively(DependencyObject root)
{
List<DependencyObject> children = new List<DependencyObject>();
children.Add(root);
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++)
children.AddRange(GetChildrenRecursively(VisualTreeHelper.GetChild(root, i)));
return children;
}
Use this method like this to find all TextBoxes:
var textBoxes = GetChildrenRecursively(LayoutRoot).OfType<TextBox>();
It sounds like you need a recursive routine like GetTextBoxes below:
void Page_Loaded(object sender, RoutedEventArgs e)
{
// Instantiate a list of TextBoxes
List<TextBox> textBoxList = new List<TextBox>();
// Call GetTextBoxes function, passing in the root element,
// and the empty list of textboxes (LayoutRoot in this example)
GetTextBoxes(this.LayoutRoot, textBoxList);
// Now textBoxList contains a list of all the text boxes on your page.
// Find all the non empty textboxes, and put them into a list.
var nonEmptyTextBoxList = textBoxList.Where(txt => txt.Text != string.Empty).ToList();
// Do something with each non empty textbox.
nonEmptyTextBoxList.ForEach(txt => Debug.WriteLine(txt.Text));
}
private void GetTextBoxes(UIElement uiElement, List<TextBox> textBoxList)
{
TextBox textBox = uiElement as TextBox;
if (textBox != null)
{
// If the UIElement is a Textbox, add it to the list.
textBoxList.Add(textBox);
}
else
{
Panel panel = uiElement as Panel;
if (panel != null)
{
// If the UIElement is a panel, then loop through it's children
foreach (UIElement child in panel.Children)
{
GetTextBoxes(child, textBoxList);
}
}
}
}
Instantiate an empty list of TextBoxes. Call GetTextBoxes, passing in the root control on your page (in my case, that's this.LayoutRoot), and GetTextBoxes should recursively loop through every UI element that is a descendant of that control, testing to see if it's either a TextBox (add it to the list), or a panel, that might have descendants of it's own to recurse through.
Hope that helps. :)
From your top most panel you can do this (my grid is called ContentGrid)
var textBoxes = this.ContentGrid.Children.OfType<TextBox>();
var nonEmptyTextboxes = textBoxes.Where(t => !String.IsNullOrEmpty(t.Text));
foreach (var textBox in nonEmptyTextboxes)
{
//Do Something
}
However this will only find the textboxes that are immediate children. Some sort of recursion like below would help, but I'm thinking there must be a better way.
private List<TextBox> SearchForTextBoxes(Panel panel)
{
List<TextBox> list = new List<TextBox>();
list.AddRange(panel.Children.OfType<TextBox>()
.Where(t => !String.IsNullOrEmpty(t.Text)));
var panels = panel.Children.OfType<Panel>();
foreach (var childPanel in panels)
{
list.AddRange(SearchForTextBoxes(childPanel));
}
return list;
}
Took Scott's initial idea and expanded it so that it
Uses generics, so it easily copes with multiple control types.
Supports more container types. In my WP7 I needed to support panaorama's, scroll viewers etc... which aren't Panels. So this allows support for them.
Biggest issue is that string comparing, especially on the Panel and derrived items.
Code:
private static void GetControls<T>(UIElement uiElement, List<T> controlList) where T : UIElement
{
var frameworkFullName = uiElement.GetType().FullName;
if (frameworkFullName == typeof(T).FullName)
{
controlList.Add(uiElement as T);
return;
}
if (frameworkFullName == typeof(Panel).FullName ||
frameworkFullName == typeof(Grid).FullName ||
frameworkFullName == typeof(StackPanel).FullName)
{
foreach (var child in (uiElement as Panel).Children)
{
GetControls(child, controlList);
}
return;
}
if (frameworkFullName == typeof(Panorama).FullName)
{
foreach (PanoramaItem child in (uiElement as Panorama).Items)
{
var contentElement = child.Content as FrameworkElement;
if (contentElement != null)
{
GetControls(contentElement, controlList);
}
}
return;
}
if (frameworkFullName == typeof(ScrollViewer).FullName)
{
var contentElement = (uiElement as ScrollViewer).Content as FrameworkElement;
if (contentElement != null)
{
GetControls(contentElement, controlList);
}
return;
}
}
Similar logic to ideas above to also handle controls with a "Content" attribute like TabItems and Scrollviewers where children might be embedded at a lower level. Finds all children:
IEnumerable<DependencyObject> GetControlsRecursive(DependencyObject root)
{
List<DependencyObject> elts = new List<DependencyObject>();
elts.Add(root);
string type = root.GetType().ToString().Replace("System.Windows.Controls.", "");
switch (root.GetType().ToString().Replace("System.Windows.Controls.", ""))
{
case "TabItem":
var TabItem = (TabItem)root;
elts.AddRange(GetControlsRecursive((DependencyObject)TabItem.Content));
break;
case "ScrollViewer":
var Scroll = (ScrollViewer)root;
elts.AddRange(GetControlsRecursive((DependencyObject) Scroll.Content));
break;
default: //controls that have visual children go here
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(root); i++) elts.AddRange(GetControlsRecursive(VisualTreeHelper.GetChild(root, i)));
break;
}
return elts;
}
I am dropping something in a ListView in WPF. I need to know the item in the (X,Y) position I am dropping. How can I do this?
The WPF ListView doesn't have GetItemAt. I return to my original problem.
Done! Thanks to this article
http://www.codeproject.com/KB/WPF/WPF_Drag_And_Drop_Sample.aspx
private int GetCurrentIndex(GetPositionDelegate getPosition)
{
int index = -1;
for (int i = 0; i < clasesListView.Items.Count; ++i)
{
ListViewItem item = GetListViewItem(i);
if (this.IsMouseOverTarget(item, getPosition))
{
index = i;
break;
}
}
return index;
}
private bool IsMouseOverTarget(Visual target, GetPositionDelegate getPosition)
{
Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
Point mousePos = getPosition((IInputElement)target);
return bounds.Contains(mousePos);
}
delegate Point GetPositionDelegate(IInputElement element);
ListViewItem GetListViewItem(int index)
{
if (clasesListView.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
return null;
return clasesListView.ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem;
}
Hoops, sorry. This should work fine:
FrameworkElement element = (FrameworkElement)e.OriginalSource;
ListViewItem lvi = (ListViewItem)listView1.ItemContainerGenerator.ContainerFromItem(element.DataContext);
You want to use the GetItemAt function. You may also need to call the PointToClient function before the GetItemAt since you need to work with client coordinates.