WPF Fire event for dynamically added control in stack panel - c#

I am adding adding a radio button to stack panel for each item in List and that happens without issue. (it adds 4 or 6 radio buttons as those would be count of items in my list).
foreach (var nearestgage in nearestgages)
{
StackPanel.Children.Add(new RadioButton { Content = nearestgage.GageSize.ToString(), Margin = new Thickness(1, 1, 1, 1) });
}
Now what i want to do is when any of these dynamically created radio buttons are selected at run time.
I want to fire a event.
So what i am thinking is i will have to attach a handler to my radio button click . I tried a few methods but not able to that. I have another radio button in the grid which does not belong to this group as well. Please suggest what would be the ideal way to do this.

Here's a little sample, adding the event to the radio button when it's generated.
XAML:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
Loaded="Window_Loaded">
<StackPanel Name="StackPanel">
</StackPanel>
</Window>
C#
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
for (int i = 1; i <= 10; i++)
{
RadioButton rb = new RadioButton();
rb.Content = "Item " + i.ToString();
rb.Click += rb_Click;
StackPanel.Children.Add(rb);
}
}
void rb_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show((sender as RadioButton).Content.ToString());
}
}
To deal with keeping the radio button actions separate, set the group name:
rb.GroupName = "Dynamic";

Related

implementing bold button for wpf richtextbox... selection disappears after applying bold

Let's say I have RichTextBox from the Windows WPF library, and I'm trying to make it so I can select text in the richtextbox and click a bold button. as soon as I click the bold button the selected text should remain except what's in the section turns bold, and when i click the bold button again the bold should disappear and the selection should remain unchanged.
Now for what I observe, I click block and the selection disappears. Here's my code to show tht:
<Window x:Class="WpfApp2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp2"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<DockPanel>
<StackPanel Orientation="Horizontal" DockPanel.Dock="Top">
<Button Name="Bold" Content="Bold" Click="Bold_Click"/>
</StackPanel>
<RichTextBox Name="RichTextBox1" />
</DockPanel>
</Window>
using System.Windows;
using System.Windows.Documents;
using System.Windows.Media;
namespace WpfApp2 {
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
RichTextBox1.CaretPosition.InsertTextInRun("Select this text with a mouse click-and-drag, "
+ "then click bold button, and observe that "
+ "selection highlight disappears");
}
private bool SelectionIsBold() {
var weight = RichTextBox1.Selection.GetPropertyValue(FontWeightProperty);
var unset = DependencyProperty.UnsetValue;
var SelectionIsBold = weight != unset && (FontWeight)weight == FontWeights.Bold;
return SelectionIsBold;
}
private void Bold_Click(object sender, RoutedEventArgs e) {
if (RichTextBox1.Selection.IsEmpty) return;
var textRange = RichTextBox1.Selection;
var len = RichTextBox1.Selection.Text.Length;
var startpos = RichTextBox1.CaretPosition.GetPositionAtOffset(0);
var endpos = RichTextBox1.CaretPosition.GetPositionAtOffset(len); //GetPoint(start, len);
if (SelectionIsBold())
RichTextBox1.Selection.ApplyPropertyValue(Inline.FontWeightProperty, FontWeights.Normal);
else
RichTextBox1.Selection.ApplyPropertyValue(Inline.FontWeightProperty, FontWeights.Bold);
// ==>> DOESN"T WORK: Attempt to Reselect Text After ApplyPropertyValue because it unhighlights the selection...
RichTextBox1.SelectionBrush = new SolidColorBrush(Colors.Red);
RichTextBox1.Selection.Select(startpos, endpos);
}
} // class
} // namespace
So the issue is not that the selection is being removed. When the user clicks on the button the RichTextBox loses focus so the selection highlight appears to go away. You don't need to worry about re-calculating the selection, just call Focus().
private void Bold_Click(object sender, RoutedEventArgs e)
{
if (RichTextBox1.Selection.IsEmpty) return;
if (SelectionIsBold())
RichTextBox1.Selection.ApplyPropertyValue(Inline.FontWeightProperty, FontWeights.Normal);
else
RichTextBox1.Selection.ApplyPropertyValue(Inline.FontWeightProperty, FontWeights.Bold);
// The selection brush obviously isn't required but i kept it to make it obvious it is working.
RichTextBox1.SelectionBrush = new SolidColorBrush(Colors.Red);
RichTextBox1.Focus(); // Return focus to the Text Box after the button click
}

SourceUpdated with NotifyOnSourceUpdated not fired in ListBox

I'm trying to use a SourceUpdated event in ListBox, but it doesn't fire.
I have binded ObservableCollection to ItemSource of ListBox, set NotifyOnSourceUpdated = true, and binding is working correctly - after adding new item to the collection, the ListBox displays new item, but without fireing the event.
MainWindow.xaml.cs:
public partial class MainWindow:Window
{
public ObservableCollection<string> Collection { get; set; }
public MainWindow()
{
InitializeComponent();
Collection = new ObservableCollection<string>();
var custBinding = new Binding()
{
Source = Collection,
NotifyOnSourceUpdated = true
};
intList.SourceUpdated += intList_SourceUpdated;
intList.SetBinding(ItemsControl.ItemsSourceProperty, custBinding);
}
private void intList_SourceUpdated(object sender, DataTransferEventArgs e)
{
MessageBox.Show("Updated!");
}
private void btnAddInt_Click(object sender, RoutedEventArgs e)
{
var randInt = new Random().Next();
Collection.Add(randInt.ToString());
}
}
MainWindow.xaml:
<Window x:Class="Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Test"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Button x:Name="btnAddInt" Content="Button" HorizontalAlignment="Left" Margin="41,31,0,0" VerticalAlignment="Top" Width="75" Click="btnAddInt_Click"/>
<ListBox x:Name="intList" HorizontalAlignment="Left" Height="313" Margin="160,31,0,0" VerticalAlignment="Top" Width="600"/>
</Grid>
What am I missing, that it's not working ?
Thank you for an advice.
SourceUpdated is only fired on elements that can accept input and change the databound source value directly.
https://msdn.microsoft.com/en-us/library/system.windows.data.binding.sourceupdated(v=vs.110).aspx
In this case, the listbox itself is not updating the collection, rather the collection is being updated via a button click. This has nothing to do with the listbox firing the SourceUpdated Event.
Only input elements that can accept input like a textboxes, checkboxes, radio buttons and custom controls that use those controls will be able to two-way bind and transfer their values back to the source it is bound to.
You may be looking for CollectionChanged which will fire when items are added or removed from the Collection.
https://msdn.microsoft.com/en-us/library/ms653375(v=vs.110).aspx
Collection = new ObservableCollection<string>();
Collection.CollectionChanged += (s, e) =>
{
// collection changed!
};
Hope this helps! Cheers!

C# WPF Can't click button after defining margin

I have created a button in my WPF Application using the following code:
Button EditButton = new Button();
EditButton.Margin = new System.Windows.Thickness(Location[0], Location[1], 0, 0);
EditButton.Height = double.Parse("20");
EditButton.Width = double.Parse("20");
EditButton.Cursor = System.Windows.Input.Cursors.Hand;
EditButton.Content = "TEST!";
EditButton.Click += new System.Windows.RoutedEventHandler(Edit_Click);
Grid.Children.Add(EditButton);
Location[1] += 17;
The button works perfectly when I have not defined EditButton.Margin but as soon as I define it I can't click it and the cursor does not change. I have searched the internet around for an answer and none of them seemed to work. Thanks in advance.
If you cannot click the control you have created, then it is generally being caused by other control being on top of it.
I would suggest altering your code slightly and move on from that point:
var stackPanel = new StackPanel();
var button = new Button();
button.Content = "Your Button";
button.Click += new System.Windows.RoutedEventHandler(Edit_Click);
stackpanel.Children.Add(button);
I suggest using StackPanel as it automatically arrange your control and thus prevents it from overlapping you can start from this point on see whether issue was caused by grid or some other component.
Button will stretch by default to its content, so will StackPanel.
Not sure what 'Location' is in your code, and I assume 'Grid' is the name of the grid. The below works.
public MainWindow()
{
InitializeComponent();
Button EditButton = new Button();
EditButton.Margin = new System.Windows.Thickness(10, 10, 0, 0);
EditButton.Height = double.Parse("20");
EditButton.Width = double.Parse("20");
EditButton.Cursor = System.Windows.Input.Cursors.Hand;
EditButton.Content = "TEST!";
EditButton.Click += new System.Windows.RoutedEventHandler(Edit_Click);
Grid.Children.Add(EditButton);
// Location[1] += 17;
}
private void Edit_Click(object sender, RoutedEventArgs e)
{
throw new NotImplementedException();
}
XAML -
<Window x:Class="WpfApplication6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid x:Name="Grid">
</Grid>
</Window>
It looks like you want to do this programmatically, but if you define it in XAML, you could set the button's Panel.ZIndex property to some high number to bring it to the front:
<Button Content="TEST!" Panel.ZIndex="1000" Height="20" Width="20" Cursor="Hand" Click="Edit_Click" />
Hope that helps somebody...

Declaratively adding row and controls to grid in WPF

Well, my last question did not garner any response except a downvote, so I am going to break down the question and ask one elementary question at a time.
I have a WPF Application, in which there is a Grid (not DataGrid). Now I want to add rows to it at runtime. And in each of those rows will be a control like TextBox or Combobox. I understand this can be done by RowDefinitions.Add(New RowDefinition()), and then adding individual controls to each cell in the row, like we did in TableLayoutPanel in WinForms. But I was looking for a more elegant solution, where all controls with their event handlers would be added to the respective cells in a new row of the Grid when a button is clicked or an event is triggered. Is there any easy way to do it?
P.S. I also need to delete the rows as required, if that is a factor here. And the row deleted is not always the last row.
Normally, it is better to use DataGrid to display data with repeating rows, not
a Grid. But if you have special reasons to use a Grid, I have attached a code sample using a Grid, displaying in each row a Textbox, a ComboBox and a DeleteButton. An Add button allows to add new rows at the bottom and the DeleteButtons on every row allow the deletion of that row.
XAML:
<Window x:Class="GridAddRow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:GridAddRow"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<Grid Name="MainGrid" DockPanel.Dock="Top"/>
<Button DockPanel.Dock="Top" Name="AddButton" Content="Add"/>
<Rectangle Fill="Gainsboro"/>
</DockPanel>
</Window>
C#:
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
namespace GridAddRow {
public partial class MainWindow: Window {
int textCol, comboBoxCol, buttonCol;
public MainWindow() {
InitializeComponent();
//initialise MainGrid, i.e. define columns, could be done in XAML
addCol(out textCol, new GridLength(100));
addCol(out comboBoxCol, GridLength.Auto);
addCol(out buttonCol, GridLength.Auto);
//initialise grid content, usually read from a db
addRow("Some Text", 1);
addRow("Another String", 3);
AddButton.Click += AddButton_Click;
}
private void addCol(out int textCol, GridLength gridLength) {
textCol = MainGrid.ColumnDefinitions.Count;
MainGrid.ColumnDefinitions.Add(new ColumnDefinition { Width = gridLength });
}
List<TextBox> textBoxes = new List<TextBox>();
List<ComboBox> comboBoxes = new List<ComboBox>();
private void addRow(string textBoxString, int comboBoxIndex) {
int rowId = MainGrid.RowDefinitions.Count;
MainGrid.RowDefinitions.Add(new RowDefinition());
TextBox textBox = new TextBox { Text = textBoxString };
textBoxes.Add(textBox);
addControl(textBox, rowId, textCol);
textBox.TextChanged += TextBox_TextChanged;
ComboBox comboBox = new ComboBox {SelectedIndex= comboBoxIndex };
comboBox.Items.Add(new ComboBoxItem {Content="0" });
comboBox.Items.Add(new ComboBoxItem { Content="1" });
comboBox.Items.Add(new ComboBoxItem { Content="2" });
comboBox.Items.Add(new ComboBoxItem { Content="3" });
comboBoxes.Add(comboBox);
addControl(comboBox, rowId, comboBoxCol);
comboBox.SelectionChanged += ComboBox_SelectionChanged;
Button deleteRowButton = new Button {Content = "Delete"};
addControl(deleteRowButton, rowId, buttonCol);
deleteRowButton.Click += DeleteRowButton_Click;
}
private void addControl(Control control, int rowId, int textCol) {
control.Tag = rowId;
MainGrid.Children.Add(control);
Grid.SetRow(control, rowId);
Grid.SetColumn(control, textCol);
}
private void TextBox_TextChanged(object sender, TextChangedEventArgs e) {
TextBox textbox = (TextBox)sender;
int rowid = (int)textbox.Tag;
//do something here.
}
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
ComboBox comboBox = (ComboBox)sender;
int rowid = (int)comboBox.Tag;
//do something here.
}
private void DeleteRowButton_Click(object sender, RoutedEventArgs e) {
Button deleteButton = (Button)sender;
int rowid = (int)deleteButton.Tag;
MainGrid.Children.Remove(deleteButton);
TextBox textbox = textBoxes[rowid];
MainGrid.Children.Remove(textbox);
textBoxes.RemoveAt(rowid);
ComboBox comboBox = comboBoxes[rowid];
MainGrid.Children.Remove(comboBox);
comboBoxes.RemoveAt(rowid);
}
private void AddButton_Click(object sender, RoutedEventArgs e) {
addRow("", 1);
}
}
}
Saying "you should use MVVM" basically just means that you should create a class which has one property for every control (Textbox, Combobox) you use in your Window. Create a collection of that class with the data to be displayed, then bind it to the DataGrid, which will display the data and offers lots of functionality, which you have to program yourself when using a Grid. For more details see my article: Guide to WPF DataGrid formatting using bindings

WPF MenuItem vs Button behavior in new window when mouse still down

I didn't know how to make the 'issue' (different behavior) more clear in the title, but I'll explain here.
In our WPF application, we're using the DataGrid control to list a number of entities. After double clicking a row, we open a new Window and in that Window, there are among other things a couple of MenuItem controls.
The problem is that when the Window opens on a position where one of the menu items is right under the mouse pointer, the menu item is actually clicked there at the mouse up of the double click.
When we use a button instead, the button click event is not fired automatically in the same situation.
We are now thinking about using buttons instead of menu items (or creating custom menus), but perhaps someone here has an explanation or solution to alter this behavior? Personally, I can't think of a case where this would be beneficial at all. Thanks in advance!
Example code is below. To see what I mean, double click the DataGrid row to open the new window and hold down the mouse button, move to the menu item and let the mouse button go (in TestWindow.xaml, exchange the MenuItem for a Button control to see the difference in behavior):
MainWindow.xaml
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style x:Key="DataGridRowStyle"
TargetType="{x:Type DataGridRow}">
<EventSetter Event="MouseDoubleClick" Handler="DataGridRow_MouseDoubleClick" />
</Style>
</Window.Resources>
<DataGrid RowStyle="{StaticResource DataGridRowStyle}" x:Name="MyDataGrid">
<DataGrid.Columns>
<DataGridTextColumn Header="String" Binding="{Binding}" IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
ObservableCollection<string> myCollection = new ObservableCollection<string>();
myCollection.Add("test");
MyDataGrid.ItemsSource = myCollection;
this.DataContext = this;
}
private void DataGridRow_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
TestWindow window = new TestWindow();
window.Show();
window.Activate();
}
}
TestWindow.xaml
<Window x:Class="WpfApplication2.TestWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="TestWindow" Height="300" Width="300">
<Grid>
<MenuItem Header="Test" Click="Button_Click" />
</Grid>
TestWindow.xaml.cs
public partial class TestWindow : Window
{
public TestWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
You could try modifying your DataGridRow_MouseDoubleClick handler as follows:
private void DataGridRow_MouseDoubleClick( object sender, MouseButtonEventArgs e )
{
this.Dispatcher.BeginInvoke(
( Action )delegate
{
TestWindow window = new TestWindow();
window.Show();
window.Activate();
},
System.Windows.Threading.DispatcherPriority.Background,
null
);
}
That might solve the problem by letting the outstanding events "settle" before creating the new window.

Categories