How to prevent WPF Gridviewcolumn collapse fully - wpf

I have Gridviewcolumns defined in the GridView. I want to prevent the gridcolumns collapse fully with column re-order enabled. I tried to set minimum width to GridViewColumnHeader but still I could see GridViewColumn collapsed.
I tried to set the IsHitTestVisible="False" but GridViewColumn couldn't achieve it.

Finally I was able to set the column size so that they are collapsed on dragging.
Here is the sample code I added in code behind.There might be some other way through xaml but I didn't get the way to do it.
private void connectedReadersListView_DragDelta(object sender, System.Windows.Controls.Primitives.DragDeltaEventArgs e)
{
Thumb senderAsThumb = e.OriginalSource as Thumb;
GridViewColumnHeader header = senderAsThumb.TemplatedParent as GridViewColumnHeader;
if (header.Content.ToString() == "System.Windows.Controls.Button: READ" && header.Column.ActualWidth < 60)
{
header.Column.Width = 60;
}
if (header.Content.ToString() == "System.Windows.Controls.Button: DISCONNECT" && header.Column.ActualWidth < 160)
{
header.Column.Width = 160;
}
if (header != null && header.Content != null)
{
switch (header.Content.ToString().Trim().ToLower())
{
case "reader name":
if (header.Column.ActualWidth < 150)
{
header.Column.Width = 150;
}
break;
case "model":
if (header.Column.ActualWidth < 50)
{
header.Column.Width = 50;
}
break;

Related

Overridden CellPainting in DataGridView displays content only after cell loses focus

I have overridden the CellPainting in a WinForms DataGridView to display a specific image in a cell depending on various factors.
To give you some more details, in the CellPainting I am redrawing the content of a DataGricViewCheckBoxColumn; I want to display a green tick or red cross instead of the default black tick.
To draw the image I use:
e.Graphics.DrawImage(image, new PointF(centreX - offsetX, centreY - offsetY));
It works fine, but my green tick/red cross is only displayed after the cell loses focus. Is there a way to make it show as soon as I have clicked it, just like the standard checkbox does?
Thanks
Handle the CellContentClick event to toggle the value (true/false) of the current cell. The CellContent of a DataGridViewCheckBoxColumn is the check box.
Assuming the DataGridViewCheckBoxColumn is the first column:
private void dgv_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == 0 && e.RowIndex >= 0)
{
var s = sender as DataGridView;
var b = s[e.ColumnIndex, e.RowIndex].Value == null
? true
: (bool)s[e.ColumnIndex, e.RowIndex].Value;
s[e.ColumnIndex, e.RowIndex].Value = !b;
}
}
If you prefer to toggle the value by clicking anywhere on the cell (not over the check box only), handle the CellMouseClick instead:
private void dgv_CellMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.Button == MouseButtons.Left && e.ColumnIndex == 0 && e.RowIndex >= 0)
{
var s = sender as DataGridView;
var b = s[e.ColumnIndex, e.RowIndex].Value == null
? true
: (bool)s[e.ColumnIndex, e.RowIndex].Value;
s[e.ColumnIndex, e.RowIndex].Value = !b;
s.NotifyCurrentCellDirty(true);
}
}
Either way, the CellPainting event fires and the specified image is drawn as you click.
CellPainting example:
Bitmap bmp1 = Properties.Resources.GreenImage;
Bitmap bmp2 = Properties.Resources.RedImage;
private void dgv_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
if (e.ColumnIndex == 0 && e.RowIndex >= 0)
{
var s = sender as DataGridView;
e.Paint(e.CellBounds, DataGridViewPaintParts.Background
| DataGridViewPaintParts.Border
| DataGridViewPaintParts.SelectionBackground
| DataGridViewPaintParts.Focus
| DataGridViewPaintParts.ErrorIcon);
if (e.Value != null)
{
var r = new Rectangle((e.CellBounds.Width - 16) / 2 + e.CellBounds.X,
(e.CellBounds.Height - 16) / 2 + e.CellBounds.Y, 16, 16);
var b = (bool)e.Value;
e.Graphics.DrawImage(b ? bmp1 : bmp2, r);
}
e.Handled = true;
}
}

Parent WPF ListBox doesnt Scroll when the mouse cursor is over a item of its ListBox inside?

I have a ListBox(Parent) and a ListBox inside it(Child). I can't scroll through the items of parent ListBox if the mouse cursor is over a item of child ListBox. How to we can scroll through the parent control irrespective of the item under the mouse cursor. Thanks for help me!
i found a solution for this issues:
private void listBox1_PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
var scrollViwer = GetScrollViewer(sender as DependencyObject) as ScrollViewer;
if (scrollViwer != null)
{
if (e.Delta < 0)
{
scrollViwer.ScrollToVerticalOffset(scrollViwer.VerticalOffset + 3);
}
else if (e.Delta > 0)
{
scrollViwer.ScrollToVerticalOffset(scrollViwer.VerticalOffset - 3);
}
}
}
public static DependencyObject GetScrollViewer(DependencyObject o)
{
if (o is ScrollViewer)
{ return o; }
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++)
{
var child = VisualTreeHelper.GetChild(o, i);
var result = GetScrollViewer(child);
if (result == null)
{
continue;
}
else
{
return result;
}
}
return null;
}
Easiest way to solve this:
Set IsEnabled=false on Child Listbox
but I don't now what is your final goal. Btw ScrollViewer (Attached property of ListBox) has tons of property to setup your prefered behavior.

Unable to get exact WPF DataGrid embedded control type From DataGridTemplateColumn

public void MouseSingleClickEditable(object sender, MouseButtonEventArgs e)
{
if ((!datagrid.HasItems) || datagrid.SelectedIndex < 0) return;
DataGridColumn clmn = datagrid.CurrentColumn;
if (clmn != null)
{
String columnType = clmn.GetType().Name;
switch (columnType)
{
case "DataGridTemplateColumn":
case "DataGridCheckBoxColumn":
row = (DataGridRow)datagrid.ItemContainerGenerator.ContainerFromItem(datagrid.Items[datagrid.SelectedIndex]);
if (!row.IsEditing)
{
datagrid.IsReadOnly = false;
datagrid.BeginEdit();
}
break;
default:
break;
}
}
}
I have 2 DatePickers, Checkbox and a combobox in my WPF Datagrid but while geeting the column type I am getting it only as DataGridTemplateColumn instead of type DataGridDatePickerColumn or DataGridComboboxColumn. How to extract the exact type of Control from the DataGridTemplateColumn.
public void MouseSingleClickEditable(object sender, MouseButtonEventArgs e) {
var datagrid = new DataGrid();
if ((!datagrid.HasItems) || datagrid.SelectedIndex < 0) return;
DataGridColumn clmn = datagrid.CurrentColumn;
if (clmn != null) {
if (clmn is DataGridCheckBoxColumn) {
//do something
} else if (clmn is DataGridTemplateColumn) {
var templateColumn = (DataGridTemplateColumn)clmn;
var rootControlOfCellTemplate = templateColumn.CellTemplate.LoadContent();
var rootControlOfCellEditingTemplate = templateColumn.CellEditingTemplate.LoadContent();
// you can now check for types of the template. CellEditingTemplate is for template in edit mode, and CellTemplate for "non-edit" mode
// for example
if (rootControlOfCellTemplate is Button) {
//do something
}
if (rootControlOfCellEditingTemplate is DatePicker) {
//do something
}
}
}
}
Note how I check for types. Normally you should do it like this, not by hard-coded strings.

How can we set the Wrap-point for the WrapPanel?

I have an ItemsControl and I want the data to be entered into two columns. When the User resizes to a width lesser than the second column, the items of the second column must wrap into the first column. Something like a UniformGrid but with wrapping.
I have managed to use a WrapPanel to do this. But I am forced to manipulate and hard code the ItemWidth and MaxWidth of the WrapPanel to achieve the two columns thing and the wrapping. This is not a good practice.
Is there anyway to set the Maximum no of columns or in other words allowing us to decide at what point the WrapPanel should start wrapping?
Some browsing on the internet revealed that the WrapGrid in Windows 8 Metro has this property. Does anyone have this implementation in WPF?
There's an UniformWrapPanel article on codeproject.com, which I modified to ensure the items in a wrap panel have uniform width and fit to the width of the window. You should easily be able to modify this code to have a maximum number of columns. Try changing the code near
var itemsPerRow = (int) (totalWidth/ItemWidth);
in order to specify the max columns.
Here's the code:
public enum ItemSize
{
None,
Uniform,
UniformStretchToFit
}
public class UniformWrapPanel : WrapPanel
{
public static readonly DependencyProperty ItemSizeProperty =
DependencyProperty.Register(
"ItemSize",
typeof (ItemSize),
typeof (UniformWrapPanel),
new FrameworkPropertyMetadata(
default(ItemSize),
FrameworkPropertyMetadataOptions.AffectsMeasure,
ItemSizeChanged));
private static void ItemSizeChanged(
DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
var uniformWrapPanel = sender as UniformWrapPanel;
if (uniformWrapPanel != null)
{
if (uniformWrapPanel.Orientation == Orientation.Horizontal)
{
uniformWrapPanel.ItemWidth = double.NaN;
}
else
{
uniformWrapPanel.ItemHeight = double.NaN;
}
}
}
public ItemSize ItemSize
{
get { return (ItemSize) GetValue(ItemSizeProperty); }
set { SetValue(ItemSizeProperty, value); }
}
protected override Size MeasureOverride(Size availableSize)
{
var mode = ItemSize;
if (Children.Count > 0 && mode != ItemSize.None)
{
bool stretchToFit = mode == ItemSize.UniformStretchToFit;
if (Orientation == Orientation.Horizontal)
{
double totalWidth = availableSize.Width;
ItemWidth = 0.0;
foreach (UIElement el in Children)
{
el.Measure(availableSize);
Size next = el.DesiredSize;
if (!(Double.IsInfinity(next.Width) || Double.IsNaN(next.Width)))
{
ItemWidth = Math.Max(next.Width, ItemWidth);
}
}
if (stretchToFit)
{
if (!double.IsNaN(ItemWidth) && !double.IsInfinity(ItemWidth) && ItemWidth > 0)
{
var itemsPerRow = (int) (totalWidth/ItemWidth);
if (itemsPerRow > 0)
{
ItemWidth = totalWidth/itemsPerRow;
}
}
}
}
else
{
double totalHeight = availableSize.Height;
ItemHeight = 0.0;
foreach (UIElement el in Children)
{
el.Measure(availableSize);
Size next = el.DesiredSize;
if (!(Double.IsInfinity(next.Height) || Double.IsNaN(next.Height)))
{
ItemHeight = Math.Max(next.Height, ItemHeight);
}
}
if (stretchToFit)
{
if (!double.IsNaN(ItemHeight) && !double.IsInfinity(ItemHeight) && ItemHeight > 0)
{
var itemsPerColumn = (int) (totalHeight/ItemHeight);
if (itemsPerColumn > 0)
{
ItemHeight = totalHeight/itemsPerColumn;
}
}
}
}
}
return base.MeasureOverride(availableSize);
}
}

WPF DataGrid doesn't shrink when column width shrinks

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

Resources