Add multiple buttons to WPF Datagrid - c#

I want to add 2-3 buttons to the rows in the last column of my datagrid using the backend C# and not XAML. I managed to add one button to the cells but I'm having trouble adding anymore past that.
I have tried creating a new FrameworkElementFactory and adding it into the column but it just replaces the previous button instead of adding the button.
DataGridTemplateColumn buttonColumn = new DataGridTemplateColumn();
buttonColumn.Header = "Actions";
buttonColumn.Width = 209;
DataTemplate buttonTemplate = new DataTemplate();
FrameworkElementFactory buttonFactory = new FrameworkElementFactory(typeof(Button));
buttonTemplate.VisualTree = buttonFactory;
buttonFactory.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Activate));
buttonFactory.SetValue(ContentProperty, "A");
buttonColumn.CellTemplate = buttonTemplate;
dGrid_SavedData.Columns.Add(buttonColumn);

Regardless of whether you create it programmatically or in XAML, a DataTemplate can only have single root element so you should set the VisualTree property to a FrameworkElementFactory for a Panel and use the AppendChild method to add the button factories to the panel factory, e.g.:
DataGridTemplateColumn buttonColumn = new DataGridTemplateColumn();
buttonColumn.Header = "Actions";
buttonColumn.Width = 209;
DataTemplate buttonTemplate = new DataTemplate();
FrameworkElementFactory panelFactory = new FrameworkElementFactory(typeof(StackPanel));
buttonTemplate.VisualTree = panelFactory;
FrameworkElementFactory buttonAFactory = new FrameworkElementFactory(typeof(Button));
buttonAFactory.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Activate));
buttonAFactory.SetValue(ContentProperty, "A");
FrameworkElementFactory buttonBFactory = new FrameworkElementFactory(typeof(Button));
buttonBFactory.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(Activate));
buttonBFactory.SetValue(ContentProperty, "B");
panelFactory.AppendChild(buttonAFactory);
panelFactory.AppendChild(buttonBFactory);
buttonColumn.CellTemplate = buttonTemplate;
dGrid_SavedData.Columns.Add(buttonColumn);

So, I’m on an iPad, so I can’t give much detail. First let me say that this is a very hard way to do this and I recommend just about anything else. But if you absolutely have to fully dynamically generate your grid, you need to put the button in a container. For example, a vertical stack panel. Then you can add as many buttons as you want. Good luck!

Here is the Code Snippet
<DataGrid x:Name="dgStudent" d:ItemsSource="{d:SampleData ItemCount=5}" Margin="0,44,0,0" Grid.RowSpan="2">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Edit">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Name="btnEdit" Content="Edit" Click="btnEdit_Click" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Delete">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Name="btnDelete" Content="Delete" Click="btnDelete_Click" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>

Related

Reset binding in code

I'm writing an application that is able to switch language using this.
I also have a combox with two features:
supply help text if no item is selected in combobox (see this)
items are selectable with checkboxes (see this)
My combobox looks like
<Grid>
<ComboBox x:Name="MyCB" SelectionChanged="OnCbObjectsSelectionChanged" ...>
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding IsSelected}" Checked="OnCbObjectCheckBoxChecked" Unchecked="OnCbObjectCheckBoxChecked" Width="20" />
<TextBlock Text="{Binding Value}" Width="100" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Visibility="{Binding SelectedItem, ElementName=MyCB, Converter={StaticResource Null2Vis}}"
IsHitTestVisible="False"
VerticalAlignment="Center"
Name="tbObjects"
Text="{ns:Loc Var1}"
FontSize="20"
Foreground="Gray"/>
</Grid>
I temporary deactivated the converter with return Visibility.Visible; with no effect.
Whenever I check some checkboxes the combobox.Text property is set and the binding from ns:Loc is overriden. How can I set it again in code if all checkboxes are unchecked?
private void OnCbObjectCheckBoxChecked(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
foreach (var cbObject in MyCB.Items)
if (cbObject.IsSelected)
sb.AppendFormat("{0}, ", cbObject.Value);
tbObjects.Text = sb.ToString().Trim().TrimEnd(',');
if (tbObjects.Text == "")
{
Binding myBinding = new Binding();
myBinding.Source = TranslationSource.Instance["Var1"]; // <- this does not work :/
myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(tbObjects, TextBox.TextProperty, myBinding);
tbObjects.Foreground = new SolidColorBrush(Colors.Gray);
}
}
When all checkboxes are unchecked there is no text in the combobox.
What am I missing?
Edit: Added TextBox element to code
If I understand your code correctly to restore the localization binding you don't need to redefine the Binding - it should be sufficient to use LocExtension with proper argument, because it derives from Binding:
if (tbObjects.Text == "")
{
var myBinding = new LocExtension("Var1");
BindingOperations.SetBinding(tbObjects, TextBox.TextProperty, myBinding);
tbObjects.Foreground = new SolidColorBrush(Colors.Gray);
}
But if for some reason you still want to redefine the Binding, here's what it should look like:
if (tbObjects.Text == "")
{
Binding myBinding = new Binding("[Var1]");
myBinding.Source = TranslationSource.Instance;
myBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
BindingOperations.SetBinding(tbObjects, TextBox.TextProperty, myBinding);
tbObjects.Foreground = new SolidColorBrush(Colors.Gray);
}
Note that the source for the binding is TranslationSource.Instance, and the path should be "[Var1]" to indicate its indexer with "Var1" argument.
Side note
Since TranslationSource.Item[string] indexer is read-only, setting the Binding.UpdateSourceTrigger is reduntant. Also, for the same reason, I'd set Binding.Mode to OneWay.

Image in Tooltip of a datagrid cell

I need to have a datagrid column with some image, and for each cell I need to see, in the tooltip, the bigger image. The datagrid is binded on a ObsevarbleCollection of MyOBjects, and MyImage and MyBigImage are poperties of MyObject. With XAML here there is the working code:
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding MyImage}" >
<Image.ToolTip>
<ToolTip >
<StackPanel>
<Image Source="{Binding MyBigImage}" />
</StackPanel>
</ToolTip>
</Image.ToolTip>
</Image>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
But I need to move the code in the c# side. So I've tried this code:
DataGridTemplateColumn col1 = new DataGridTemplateColumn();
FrameworkElementFactory factoryImg = new FrameworkElementFactory(typeof(Image));
Binding b1 = new Binding("MyImage");
b1.Mode = BindingMode.TwoWay;
factoryImg.SetValue(Image.SourceProperty, b1);
DataTemplate ttTemplate = new DataTemplate(typeof(StackPanel));
FrameworkElementFactory spFactory = new FrameworkElementFactory(typeof(StackPanel));
spFactory.SetValue(StackPanel.OrientationProperty, Orientation.Horizontal);
FrameworkElementFactory imgHolder = new FrameworkElementFactory(typeof(Image));
Binding b2 = new Binding("MyBigImage");
b2.Mode = BindingMode.TwoWay;
imgHolder.SetValue(TextBlock.DataContextProperty, this.pieces);
imgHolder.SetBinding(Image.SourceProperty, b2);
spFactory.AppendChild(imgHolder);
ttTemplate.VisualTree = spFactory;
ToolTip tt = new System.Windows.Controls.ToolTip();
tt.ContentTemplate = ttTemplate;
factoryImg.SetValue(TextBlock.ToolTipProperty, tt);
DataTemplate cellTemplate1 = new DataTemplate();
cellTemplate1.VisualTree = factoryImg;
col1.CellTemplate = cellTemplate1;
grdDataItems.Columns.Add(col1);
cellTemplate1.Seal();
In the datagrid cell of the column I see the correct image from "MyImage", but in the tooltip of the cell I see always the image of the first element of the collection of "MyBigImage".
How can I solve this?
Regards

Change DataGrid ItemsSource binding in code-behind

I need to be able to change the binding of a DataGrid in the code-behind if the DataGrid does not have items within it, and vice versa.
As it stands, here is what my current attempt looks like:
C#:
List<Character> Characters = new List<Character>
{
new Character("Judge Dredd", Gender.Male, CharacterClass.Fighter),
new Character("Princess Xena", Gender.Female, CharacterClass.Fighter),
new Character("Hawkeye", Gender.Male, CharacterClass.Ranger),
new Character("Laura Croft", Gender.Female, CharacterClass.Ranger),
new Character("Merlin", Gender.Male, CharacterClass.Mage),
new Character("Wicked Witch of the West", Gender.Female, CharacterClass.Mage)
};
HeroBox.ItemsSource = Characters;
WPF:
<DataGrid x:Name="InvGrid"
Background="DimGray"
Grid.Column="1"
ItemsSource="{Binding ElementName=HeroBox, Path=SelectedValue.Inventory}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="Equip"
Binding="{Binding Path=Equipped, Mode=TwoWay}"></DataGridCheckBoxColumn>
<DataGridTextColumn Header="Name"
Binding="{Binding Path=Name, Mode=TwoWay}"></DataGridTextColumn>
<DataGridTextColumn Header="Effect"
Binding="{Binding Path=Effect, Mode=TwoWay}"></DataGridTextColumn>
<DataGridTextColumn Header="Cost"
Binding="{Binding Path=Cost, Mode=TwoWay}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Attempt to change ItemsSource of InvGrid - includes compilation errors(C#):
if (InvGrid.Items.Count == 0)
{
Binding b = new Binding("HeroBox")
{
ElementName = "HeroBox",
Path = "SelectedValue.Inventory"
};
InvGrid.ItemsSource = new Binding(b);
}
else
{
InvGrid.ItemsSource = null;
}
Essentially, I would like to achieve the same effect here:
ItemsSource="{Binding ElementName=HeroBox, Path=SelectedValue.Inventory}"
But in C#.
The InvGrid DataGrid pulls its data from the Inventory of the selected hero from the ComboBox (HeroBox) which is randomly generated at runtime. Afterwards, the DataGrid should allow for the user to enter anything without automatically generating anything, yet at the moment the DataGrid randomly generates a new random item in the inventory, allowing the user to change it from there.
You might consider binding to a list of type ObservableCollection<Character> instead of List<Character> because it has INotifyCollectionChanged implemented already which helps you inform the UI that something has changed and it needs to update.
Here some helpful links:
What is the use of an observable Collection?
For swapping out data in the DataGrid: Replace entire ObservableCollection with Another ObservableCollection
Don't bind the itemssource of the datagrid. Instead implement the selectionchanged event of the combobox and set invgrid.itemssource = ((character)HeroBox.selectedItem).inventory within it (adding the necessary code is case HeroBox.selectedItem is null)

ToolTip for ComboBox items in Silverlight

I just want to add a ToolTip for each item in a Silverlight Custom ComboBox. so when a user moves around items can see ToolTip, items in ComboBox will be of type string and the same value will be displayed as ToolTip.
You can create a tooltip by adding a simple data template -- give the template TextBlock with the tool tip added:
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock ToolTip="{Binding}" Text="{Binding}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Edit - Using Code behind
var dt = new DataTemplate();
var tb = new TextBlock();
tb.ToolTip = new Binding(".");
tb.Text = new Binding(".");
dt.VisualTree = tb;
var cb = new ComboBox();
cb.ItemTemplate = dt;

How to access textblock inside Datatemplate in combobox?

I am trying to Bind the TextBlock inside ComboBox through Code. I am able to bind the textblock sucessfully but For some reasons TextBlock doesnt display Text Values.
I have mechanism which checks for the selected values and there I am getting the selected values without any problem.
So to conclude, My binding is working fine but I am missing out something hence textblock is not displaying text that is bound with it.
This is the code I am using for Binding:
where "lObjTextBlock" is TextBlock inside of ComboBox.
TextBlock lObjTextBlock = (TextBlock)ComboBox.ItemTemplate.LoadContent();
Binding lObjBinding = new Binding();
lObjBinding.Path = new PropertyPath("[" + lObjMap.PropertyName + "]");
lObjTextBlock.SetBinding(TextBlock.TextProperty, lObjBinding);
This is the XAML for the TextBlock:
<my:HComboBox Name="cmbRefDoctor">
<my:HComboBox.ItemTemplate>
<DataTemplate>
<TextBlock x:Name="txtRefDoctorName" />
</DataTemplate>
</my:HComboBox.ItemTemplate>
</my:HComboBox>
Once again : My problem is that TextBlock is not displaying any Text althought values are being set.
Would love to get all possible suggestions. Thanks in advance.
it's one of the way to bind controls which inside datatemplate
this.DataContext = Person;
Binding binding = new Binding();
binding.Source = ob;
DataTemplate dtemp = (DataTemplate)Resources["PointTemp"];
Border bdr = dtemp.LoadContent() as Border;
TextBlock tblk = bdr.Child as TextBlock;
tblk.SetBinding(TextBlock.TextProperty, binding);
Here I used ob as double collection assign to textproperty by binding source
<UserControl.Resources>
<DataTemplate x:Key="PointTemp">
<Border Margin="0,23,0,0" Background="Transparent">
<TextBlock Text="{Binding}" Foreground="White" FontSize="28" HorizontalAlignment="Center" VerticalAlignment="Center" TextAlignment="Center"/>
</Border>
</DataTemplate>
</UserControl.Resources>
And you can assign,
if it's combobox or listbox
Here Person is a class name, or set the class name in combobox itemsource

Categories