WPF: StackOverFlowException iterating grid - wpf

This is the case:
I trying to do a extra format to my tooglesbuttons
private void PanelToggles_Checked(object sender, RoutedEventArgs e)
{
ToggleButton currentTB = sender as ToggleButton;
if (currentTB != null)
{
foreach (UIElement tb in GridToggles.Children)
{
MessageBox.Show(tb.GetType().ToString());
(tb as ToggleButton).IsChecked = false;
}
currentTB.IsChecked=true;
currentTB.FontWeight = FontWeights.Bold;
//implementation
}
}
It throws StackOverFlowException, some ideas?

Just a guess, but it seems like PanelToggles_Checked is an event called infinite times and the reason could be because you are doing currentTB.IsChecked = true; in your conditional statement which triggers the event to be called again and again....

Related

How can I validate only the controls inside a panel?

I have a form with two panels that each have a [Save] button.
How can I validate all of the controls inside each panel separately?
I was hoping that the Panel class would have a Validate() method but it doesn't. It's also not a ContainerControl so it also doesn't have a ValidateChildren method.
What's the best way to accomplish this?
If you set your Form's AutoValidate mode to EnableAllowFocusChange, and presuming you have validating events hooked up to each of the controls inside your panel, something like this:
private void tb_Validating(object sender, CancelEventArgs e)
{
TextBox tb = sender as TextBox;
if (tb != null)
{
if (tb.Text == String.Empty)
{
errorProvider1.SetError(tb, "Textbox cannot be empty");
e.Cancel = true;
}
else
errorProvider1.SetError(tb, "");
}
}
Then on the Click handler for your save button, you can just do this:
private void SaveButton_Click(object sender, EventArgs e)
{
foreach (Control c in panel1.Controls)
c.Focus();
// If you want to summarise the errors
StringBuilder errorSummary = new StringBuilder();
foreach (Control c in panel1.Controls){
String error = errorProvider1.GetError(c);
if (error != String.Empty)
errorSummary.AppendFormat("{0}{1}", errorProvider1.GetError(c), Environment.NewLine);
}
if(errorSummary.Length>0)
MessageBox.Show(errorSummary.ToString());
}
That will cause the validation to fire on each of the controls within the panel.

How can I select (with focus) the text in telerik:RadNumericUpDown

This NumericUpDown (NUD) floats over a map. When it gets visible I need to re-direct the next key-stroke inside the control overriding the current value.
With great pain I've found this solution:
private void LengthInput_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if ((bool)(e.NewValue))
{
...
LengthInputBox.ShowButtons = true;
try
{
LengthInputBox.Focus();
if (m_lengthTextBox == null)
{
LengthInputBox.ApplyTemplate();
m_lengthTextBox = LengthInputBox.Template.FindName("textbox", LengthInputBox) as TextBox;
}
if (m_lengthTextBox != null)
{
m_lengthTextBox.SelectAll();
m_lengthTextBox.Focus();
}
}
finally
{
LengthInputBox.ShowButtons = false;
}
...
NUD is the LengthInputBox control. Focus method sets the focus on the NUD buttons.
Template.FindName("textbox"... retrieve the internal TextBox of NUD. If found, or previously found, it selects all and set focus on the text.
Finally, I remove the Up/Down buttons (I don't need them. Although I've done lot of variations with or without them, their presence does not change the behavior...)
It works for the first time, but on the second attempt it fails again.
Any ideas?
Select and Focus are bit slow. Using a Dispatcher has solved the issue:
private void LengthInputBox_GotFocus(object sender, RoutedEventArgs e)
{
if (m_lengthTextBox == null)
{
LengthInputBox.ApplyTemplate();
m_lengthTextBox = LengthInputBox.Template.FindName("textbox", LengthInputBox) as TextBox;
}
if (m_lengthTextBox != null)
{
m_lengthTextBox.Focusable = true;
m_lengthTextBox.IsTabStop = true;
if (!m_lengthTextBox.IsFocused)
Dispatcher.BeginInvoke(new Action(() =>
{
var dot = m_lengthTextBox.Text.IndexOf('.');
m_lengthTextBox.Select(dot, m_lengthTextBox.Text.Length - dot);
m_lengthTextBox.Focus();
}));
}
LengthInputBox.CaptureMouse();
}
(Don't forget to release the mouse:
private void LengthInput_KeyDown(object sender, KeyEventArgs e)
{
switch (e.Key)
{
case Key.Escape:
case Key.Enter:
LengthInputBox.ReleaseMouseCapture();
ViewModel.IsLengthInputVisible = false;
e.Handled = true;
break;
}
}
)

How to get the Logical Children of a RowDefinition?

I have Extended the RowDefinition as RowDefinitionExtended and In that, when can i get the LogicalChildren belongs to this RowDefinition. I mean in which override can i get the LogicalChildren?
public class RowDefinitionExtended : RowDefinition
{
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
Loaded += OnRowDefinitionExtendedLoaded;
}
void OnRowDefinitionExtendedLoaded(object sender, RoutedEventArgs e)
{
var parent = GetUIParentCore() as Grid;
if (parent == null) return;
if (parent.Children.Cast<UIElement>().Where(c => Grid.GetRow(c) == parent.RowDefinitions.IndexOf(this)).All(ctrl => ctrl.Visibility != Visibility.Visible))
Height = new GridLength(0);
}
}
What my requirement is, I need to check all the LogicalChildren to its Visibility and Change its Height accordingly.
How could i do this? Any idea?
Update:
Code has been updated, On Load I could do this and it works fine. But my problem is, am changing the controls visibility after load... So is there any notification while changing the Visibility? am looking a event when the layout updated like..
Any event can i use it for?
You can't do that by means of a derived RowDefinition, but this little helper method should do the job (if your intention was to get all child elements in a certain row of a Grid):
public static IEnumerable<UIElement> ChildrenInRow(Grid grid, int row)
{
return grid.Children.Cast<UIElement>().Where(c => Grid.GetRow(c) == row);
}
You have to subscribe to the IsVisibleChanged handler for each element in the row when the row is loaded.
When the visibility changed, you could do whatever you need
public class RowDefinitionExtended : RowDefinition
{
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
Loaded += OnRowDefinitionExtendedLoaded;
}
void OnRowDefinitionExtendedLoaded(object sender, RoutedEventArgs e)
{
var parent = GetUIParentCore() as Grid;
if (parent == null) return;
//Subscribe to the IsVisibleChanged handler for each element in the row
var ElementInGridRow = parent.Children.Cast<UIElement>().Where(c => Grid.GetRow(c) == parent.RowDefinitions.IndexOf(this));
foreach (var element in ElementInGridRow)
{
element.IsVisibleChanged+=new DependencyPropertyChangedEventHandler(OnChildrenIsVisibleChanged);
}
}
private void OnChildrenIsVisibleChanged(object sender,DependencyPropertyChangedEventArgs e)
{
UIElement element = sender as UIElement;
//Do stuff...
var parent = GetUIParentCore() as Grid;
if (parent.Children.Cast<UIElement>().Where(c => Grid.GetRow(c) == parent.RowDefinitions.IndexOf(this)).All(ctrl => ctrl.Visibility != Visibility.Visible))
Height = new GridLength(0);
}
}

WPF datagrid collapse details row on click

I needed to collapse the details row of a WPF DataGrid when a user clicked on it, and re-display it when they clicked again. I also wanted to preserve the DataGridRoDetailsVisibilityMode of VisibleWhenSelected, using single selection.
I came up with this solution, based off of this post elsewhere: http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/0a45b3a7-46d0-45a9-84b2-0062f07f6fec#eadc8f65-fcc6-41df-9ab9-8d93993e114c
private bool _rowSelectionChanged;
private void dgCompletedJobs_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
_rowSelectionChanged = true;
}
private void dgCompletedJobsMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
DependencyObject dep = (DependencyObject)e.OriginalSource;
//navigate up the tree
while (dep != null &&
!(dep is DataGridCell) &&
!(dep is DataGridColumnHeader))
{
dep = VisualTreeHelper.GetParent(dep);
}
if (dep == null)
{
return;
}
DataGridCell dgc = dep as DataGridCell;
if (dgc != null)
{
//navigate further up the tree
while (dep != null && !(dep is DataGridRow))
{
dep = VisualTreeHelper.GetParent(dep);
}
DataGridRow dgr = dep as DataGridRow;
DataGrid dg = sender as DataGrid;
if (dg != null && dgr != null)
{
if (dgr.IsSelected && !_rowSelectionChanged)
{
dg.RowDetailsVisibilityMode =
(dg.RowDetailsVisibilityMode == DataGridRowDetailsVisibilityMode.VisibleWhenSelected)
? DataGridRowDetailsVisibilityMode.Collapsed
: DataGridRowDetailsVisibilityMode.VisibleWhenSelected;
}
else
{
dg.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.VisibleWhenSelected;
}
}
}
_rowSelectionChanged = false;
}
This appears to solve my problem nicely, but I have a haunting suspicion that this could be done more simply and elegantly, especially since I'm using MVVM on this project. However, I see this as an acceptable usage of event-driven code-behind, because it's purely presentation logic.
Does anyone have a cleaner solution?
To do this with "proper" MVVM, you should bind the RowDetailsVisibilityMode to a property on the view model:
<DataGrid x:Name="dgCompletedJobs" RowDetailsVisibilityMode="{Binding RowDetailsVisible}"/>
Your view model property would be something like:
private DataGridRowDetailsVisibilityMode _rowDetailsVisible;
public DataGridRowDetailsVisibilityMode RowDetailsVisible
{
get { return _rowDetailsVisible; }
set {
_rowDetailsVisible = value;
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs("RowDetailsVisible"));
}
}
}
To link the mouse click event to the changing of the property, you could either do some fancy attached behaviour commanding as indicated here, or just use code behind to call the view model directly (I often do this myself for simple tasks):
private void dgCompletedJobsMouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
Window1ViewModel viewModel = (Window1ViewModel)DataContext;
if (viewModel.RowDetailsVisible == DataGridRowDetailsVisibilityMode.Collapsed) {
viewModel.RowDetailsVisible = DataGridRowDetailsVisibilityMode.VisibleWhenSelected;
} else {
viewModel.RowDetailsVisible = DataGridRowDetailsVisibilityMode.Collapsed;
}
}
Why don't you use the sender param? If the event is defined on the DataGrid, the sender is always the DataGrid! Use a safe cast en check for null to be safe, but that should do the trick.
The code seems unnecessary complicated as you are working back from the original source to your DataGrid through the visual tree.
private void dataGridMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
DataGrid dg = sender as DataGrid;
if (dg == null)
return;
if (dg.RowDetailsVisibilityMode == DataGridRowDetailsVisibilityMode.VisibleWhenSelected)
dg.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed;
else
dg.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.VisibleWhenSelected;
}
I came up with a different way, but not a "proper" MVVM way since it uses code behind (as does some of the code in the proposed answers above) but it does the trick with just a few lines of code.
By coding against the PreviewMouseUp event I was able to get the exact behavior I needed. The code ensures that you've actually clicked on something in the grid and to collapse it has to be the same row already opened.
private void UIElement_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)
{
DataGrid grid = sender as DataGrid;
if (grid != null)
{
FrameworkElement element = e.OriginalSource as FrameworkElement;
if (element?.DataContext is MyCustomObject)
{
if (grid.SelectedItem == (MyCustomObject) ((FrameworkElement) e.OriginalSource).DataContext)
{
grid.SelectedIndex = -1;
e.Handled = true;
}
}
}
}
This combines the answer from Grafix with the answer from Prethen.
Use it if you want the row detail to toggle only when the row is already selected:
private void DataGrid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is DataGrid dataGrid &&
e.OriginalSource is FrameworkElement frameworkElement &&
frameworkElement.DataContext == dataGrid.SelectedItem)
{
if (dataGrid.RowDetailsVisibilityMode == DataGridRowDetailsVisibilityMode.VisibleWhenSelected)
dataGrid.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.Collapsed;
else
dataGrid.RowDetailsVisibilityMode = DataGridRowDetailsVisibilityMode.VisibleWhenSelected;
}
}

What event is called when we click on a DataGridViewComboBoxCell

I have a list of item in a DataGridViewComboBoxCell; I want to do something as soon as the user click on a value.
What is the event, or how could I do what I want?
CellValueChanged is called when we leave the cell...
You can handle the selectededindexchanged of the combobox.
private void dataGridView1_EditingControlShowing(object sender,
DataGridViewEditingControlShowingEventArgs e)
{
ComboBox cb = e.Control as ComboBox;
if (cb != null)
{
// first remove event handler to keep from attaching multiple:
cb.SelectedIndexChanged -= new
EventHandler(cb_SelectedIndexChanged);
// now attach the event handler
cb.SelectedIndexChanged += new
EventHandler(cb_SelectedIndexChanged);
}
}
void cb_SelectedIndexChanged(object sender, EventArgs e)
{
MessageBox.Show("Selected index changed");
}
Another possible way of doing it is:
private void dataGridView1_EditingControlShowing(object sender,
DataGridViewEditingControlShowingEventArgs e)
{
ComboBox cb = e.Control as ComboBox;
if (cb == null)
return;
dataGridView1.EditingControlShowing -= dataGridView1_EditingControlShowing;
cb.SelectedIndexChanged += cb_SelectedIndexChanged;
}
void cb_SelectedIndexChanged(object sender, EventArgs e)
{
MessageBox.Show("Selected index changed");
}
You are doing everything just once.

Resources