WPF - help converting XAML binding expression to codebehind - c#

Can anyone tell me what is the c# equivalent of the following snippet of XAML ??
<my:DataGridTextColumn
Visibility="{Binding Path=DataColumns[21].IsVisible, Source={StaticResource viewmodel}, Converter={StaticResource vc}}"
Binding="{Binding SdDevDuration}"
/>
Its the visibility binding I cannot get right. DataGridTextColumn is not a FrameworkElement so no SetBinding method.
Thanks in advance,
Matt

I worked this out. For anyone who's interested you can use the BindingOperation.SetBinding method.
The full code is,
var newCol = new DataGridTextColumn();
newCol.Binding = new Binding("SdDevDuration");
var visiblityBinding = new Binding("IsVisible");
visiblityBinding.Source = col;
visiblityBinding.Converter = new VisibilityConverter();
BindingOperations.SetBinding(newCol, DataGridTextColumn.VisibilityProperty, visiblityBinding);

Related

Add multiple buttons to WPF Datagrid

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>

How to dynamically create horizontal scrollable Gridview inside verticallly scollable page at Runtime

I want to create a page like Android playstore in which i have to create multiple horizontal scrollable Gridviews on the basis of data at runtime. As i am new to windows phone development i don't know how to create it dynamically. So Please provide any type of help or tutorial regarding this.
I have implemented the below code with this code i am able to produce the required result but the gridview items are not stacked horizontally.I want to make the items scroll horizontally So please provide any help with which required result can be achieved.I am attaching a screenshot for reference.
public void DesignUi()
{
GridViewItem grdItem = new GridViewItem();
for (int i = 0; i < 20; i++)
{
string backgroundColor = string.Empty;
StackPanel staParent = new StackPanel();
#region Header
StackPanel headerStack = new StackPanel();
headerStack.Background = new SolidColorBrush(Colors.Pink);
TextBlock textHeader = new TextBlock();
textHeader.Text = "Header :-" + i;
headerStack.Children.Add(textHeader);
#endregion
#region Body
StackPanel staBody = new StackPanel();
staBody.Background = new SolidColorBrush(Colors.Green);
#region Create Grid View
GridView grd = new GridView();
grd.SetValue(ScrollViewer.VerticalScrollModeProperty, ScrollMode.Disabled);
grd.SetValue(ScrollViewer.HorizontalScrollModeProperty, ScrollMode.Enabled);
ItemsPanelTemplate itmPanel = new ItemsPanelTemplate();
VirtualizingStackPanel vrStack = new VirtualizingStackPanel();
vrStack.Orientation = Orientation.Horizontal;
TextBlock textQ = new TextBlock();
textQ.Text = "";
vrStack.Children.Add(textQ);
itmPanel.SetValue(VirtualizingStackPanel.IsVirtualizingProperty, true);
itmPanel.SetValue(VirtualizingStackPanel.OrientationProperty, Orientation.Horizontal);
itmPanel.SetValue(ItemsControl.ItemContainerStyleProperty, Orientation.Horizontal);
ItemsControl itmCntrl = new ItemsControl();
itmCntrl.Items.Add(vrStack);
#region Create Gridview Items
for (int j = 0; j < 4; j++)
{
grdItem = new GridViewItem();
grdItem.Width = 100;
grdItem.Height = 150;
grdItem.Margin = new Thickness(5, 5, 5, 5);
grdItem.Background = new SolidColorBrush(Colors.Red);
TextBlock textGrd = new TextBlock();
textGrd.Text = "Item :-" + j;
grdItem.Content = textGrd;
grd.Items.Add(grdItem);
}
#endregion
#endregion
staBody.Children.Add(grd);
#endregion
staParent.Children.Add(headerStack);
staParent.Children.Add(staBody);
staLists.Children.Add(staParent);
}
}
Current Result Screenshot with the above code:---
Required Result Screenshot
I had to put this as an answer because the comment section was too limiting for this. So I better answer this and guide you towards the correct approach
OKay! there are better ways to do this. The best way and the easiest is via DataBinding. It'll reduce your code to almost nothing, and it'll be easier for you to design your GridView in XAML rather than doing it via c#. if you are not familiar with the concept of data binding and you want to implement it the way you're doing it now then I'll add to your solution that, the GridView would stack horizontally by setting the ItemsWrapGrid.Orientation property of your gridview to vertical to stack your elements horizontally and remember to set the scroll mode to horizontal too.
For the scroll mode: add the below to your GridView XAML
ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollMode="Enabled" ScrollViewer.VerticalScrollMode="Disabled"
For setting the ItemsWrapGrid Orientation Property:
string template =
"<ItemsPanelTemplate xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"><ItemsWrapGrid VerticalAlignment = \"Top\" "
+ " ItemWidth = \""
+ itemWidth
+ "\" Orientation = \"Vertical\"/></ItemsPanelTemplate> ";
yourgridview.ItemsPanel = (ItemsPanelTemplate)XamlReader.Load(template);
Please Note:
The Better and cleaner way to achieve this would be via DataBinding, The Below is the code for achieving this via DataBinding:
The XAML
<GridView Name="ViewView" HorizontalAlignment="Center" ItemTemplate="{StaticResource AllAppsTileData}" IsItemClickEnabled="True" SelectionMode="Single" Margin="10" ItemsSource="{Binding AppsToShow}" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollMode="Enabled" ScrollViewer.VerticalScrollMode="Disabled">
<GridView.ItemsPanel>
<ItemsPanelTemplate>
<ItemsWrapGrid Orientation="Vertical"/> <--Change this to Horizontal for vertically wrapping the items-->
</ItemsPanelTemplate>
</GridView.ItemsPanel>
<GridView.ItemContainerStyle>
<Style TargetType="GridViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Margin" Value="5"/>
</Style>
</GridView.ItemContainerStyle>
</GridView>
The DataTemplate
To be defined in your <Page.Resources>
<DataTemplate x:Key="AllAppsTileData">
<Grid>
<Grid.Background>
<ImageBrush Stretch="Fill" ImageSource="{Binding AppImage}"/>
</Grid.Background>
<Grid>
<Grid.Background>
<SolidColorBrush Color="Black" Opacity="0.3"/>
</Grid.Background>
<TextBlock Text="{Binding AppName}" FontSize="20" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</Grid>
</DataTemplate>
Your backing app class
public class AppDataClass
{
public string AppName { get; set; }
public string AppImage { get; set; } //The image would be something like ms-appx:///Assets/LOGO.png
}
Now that you have your architecture ready, there are two ways you can go about it from here,
You bind the ItemsSource property of the GridView to an ObservableCollection<AppDataClass> which can be populated by your code behind or preferably a ViewModel using the MVVM approach and each time the ObservableCollection<AppDataClass> changes, it raises the RasiePropertyChanged event from the interface INotifyPropertyChanged and the view automatically updates itself. This is a highly recommended approach as it keeps your UI and Business Logic on two different threads and either one of them would not interact with each other, they'll get the data via the ViewModel, this is the MVVM approach for more information on it use This article
As you explained that you're new to the Phone development, I would say forget all about the 1st point because it can be tough to grasp if you're new to the platform, what i'll recommend is the easy way,
From your code behind get the data into a List something like this,
List<AppDataClass> MyEntireData = new List<AppDataClass>();
MyEntireData = GetData();
where the GetData method is returning you a List<AppDataClass> and now simply after the MyEntireData is not empty or it's count is > 0 use, ViewView.ItemsSource = MyEntireData;
And you'll have a much more organized code which provides you the store way kinda layout.
And in future if you want to change the way the Tiles look you don't need to wrap your head to the c# generated XAML, you just need to modify the DataTemplate.
If there is anything do let me know in the comments section.

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.

Create bindings of ListView items programmatically

I have the following wpf control added to xaml:
<ListView Margin="22,80,271,12" Name="listView1" ItemsSource="{Binding}" />
I know how to create a ListView object programmatically. The only thing that I am missing is how could I add the property
ItemsSource="{Binding}"
with code to that object. I have already managed to add the columns and gridview with c#. The only thing that I am missing is to add that property ItemsSource="{Binding}"
I have tried looking for an answer here.
Shortest should be this (literal translation of XAML):
listView1.SetBinding(ListView.ItemsSourceProperty, new Binding());
listView1.ItemsSource = listView1.DataContext as IEnumerable;
Is this what you are looking for?
Binding myBinding = new Binding();
myBinding.ElementName = "item-you-are-binding-to";
myBinding.Path = new System.Windows.PropertyPath("property-you-are-binding-to");
listView1.SetBinding(ContentProperty, myBinding);
All you need to do is this:
var binding = new Binding("DataContext");
binding.Source = listView1;
listView1.SetBinding(ListView.ItemsSourceProperty, binding);

WPF Binding: Where a property contains the path to the value

I've got an expander with a couple of TextBlocks in the top bar which i'm using to give a title and a piece of key information.
Ideally i want to set the path to key piece of information, but i can't work out how to bind the path of the binding to another path (i apologise if i'm not making much sense!)
In the following xaml the first bit works, the second bit is what i'm struggling with.
<TextBlock Text="{Binding Path=Header.Title}"/>
<TextBlock Text="{Binding Path={Binding Path=Header.KeyValuePath}}"/>
KeyValuePath might contain something like "Vehicle.Registration" or "Supplier.Name" depending on the Model.
Can anyone point me in the right direction please? Any help gratefully received!
I don't think it can be done in pure XAML... Path is not a DependencyProperty (and anyway Binding is not a DependencyObject), so it can't be the target of a binding
You could modify the binding in code-behind instead
I haven't found a way to do this in XAML but I did this in code behind. Here is the approach I took.
Firstly, I wanted to do this for all items in an ItemsControl. So I had XAML like this:
<ListBox x:Name="_events" ItemsSource="{Binding Path=Events}">
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type Events:EventViewModel}">
<TextBlock Name="ActualText" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Then, in code behind construction I subscribe to the ItemContainerGenerator:
InitializeComponent();
_events.ItemContainerGenerator.StatusChanged
+= OnItemContainerGeneratorStatusChanged;
This method looks like:
private void OnItemContainerGeneratorStatusChanged(object sender, EventArgs e)
{
if (_events.ItemContainerGenerator.Status!=GeneratorStatus.ContainersGenerated)
return;
for (int i = 0; i < _viewModel.Events.Count; i++)
{
// Get the container that wraps the item from ItemsSource
var item = (ListBoxItem)_events.ItemContainerGenerator.ContainerFromIndex(i);
// May be null if filtered
if (item == null)
continue;
// Find the target
var textBlock = item.FindByName("ActualText");
// Find the data item to which the data template was applied
var eventViewModel = (EventViewModel)textBlock.DataContext;
// This is the path I want to bind to
var path = eventViewModel.BindingPath;
// Create a binding
var binding = new Binding(path) { Source = eventViewModel };
textBlock.SetBinding(TextBlock.TextProperty, binding);
}
}
If you only have a single item to set the binding upon, then the code would be quite a bit simpler.
<TextBlock x:Name="_text" Name="ActualText" />
And in code behind:
var binding = new Binding(path) { Source = bindingSourceObject };
_text.SetBinding(TextBlock.TextProperty, binding);
Hope that helps someone.

Categories