WPF - Commands and referencing objects - c#

So I have got C# 5.0 All-In-One for Dummies, but it doesn't really show a good example for what I want to achieve. Here is my XAML:
<StackPanel Grid.Column="1">
<Grid>
<Image Source="Images/039.JPG" x:Name="example_image"/>
<Image Source="Images/example.png" HorizontalAlignment="Right" VerticalAlignment="Bottom" Panel.ZIndex="999" x:Name="example_logo"/>
</Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="8*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Slider Grid.Column="0" Margin="10" Maximum="100" SmallChange="1" ToolTip="Watermark size in percent (%)" Value="{Binding Path=SliderValue, Source={x:Static Application.Current}}"/>
<TextBlock Grid.Column="1" Text="{Binding Path=SliderValue, Source={x:Static Application.Current}}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</StackPanel>
So I have got 2 images, one image 1 (the watermark) on top of the other image 2. And I have got a slider, which I have Binded to a value in my App.xaml.cs.
What I need to do is code a custom command, which will fire when the slider is moved, I need to add some parameters to this command which will be the 2 images names, so I can manipulate these controls using the command.
I can't seem to workout how I would do this, I have made a separate file for my commands called Commands.cs.
Why do I want to send the 2 images names as parameters, well so the command is reusable, if I want to use a different image control.
How would I go about doing this?

There isn't a Command property on the Slider although it's possible to add one (see https://gist.github.com/anonymous/4326429)
A simple option would be to handle the ValueChanged event on the Slider or perhaps if the SliderValue property on App.xaml.cs is a then you could fire your reusable "command" code from its setter.
Side note, you may want to look into the MVVM pattern rather than putting all your code in the code behind.

Related

Dynamically add UI elements to StackPanel after button pressed

This is my first question here :)
I'm not a professional programmer, I'm only 18 and I haven't studied at university or anything, so please don't hate me if I say something stupid :p
I'm making (or rather trying to make...) an app for Windows 10 UWP and a piece of my xaml code looks like this:
<Grid Grid.Row="1" Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="12"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="12"/>
</Grid.RowDefinitions>
<Rectangle Margin="0" Grid.Row="1" Fill="White" RadiusX="7" RadiusY="7"/>
<Grid Grid.Row="1" Margin="12,6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel>
<TextBox x:Name="textProduct" Margin="0,6,6,6" TextWrapping="Wrap" VerticalAlignment="Top" BorderBrush="#FFCECED2" FontFamily="Segoe UI Light" FontSize="17" PlaceholderText="Produkt..." BorderThickness="1"/>
</StackPanel>
<StackPanel Grid.Column="1">
<TextBox x:Name="textAdditionalInfo" Margin="6,6,0,6" TextWrapping="Wrap" VerticalAlignment="Top" BorderBrush="#FFCECED2" FontFamily="Segoe UI Light" FontSize="17" PlaceholderText="Dodatkowe info..." BorderThickness="1"/>
</StackPanel>
</Grid>
I also have an app bar at the bottom with Add and Delete buttons. I'd like to be able to dynamically add another line of TextBoxes to both StackPanels every time the user presses the Add button and Delete one every time they hit the Delete button. Unfortunetely I have no idea how to achieve this. I've tried to find an answer and I think this can be done by using UserControl, however I have no idea how to implement this.
I hope it's not too comlicated to do, because I don't want to seeem like a person that asks other people to do all my work for me...
If it's a big problem, then it doesn't even need to support deleting the TextBoxes.
I hope you understand what I mean. I'm not native english so sorry for any mistakes ;)
Welcome to XAML, it's well worth the time learning it!
For displaying data XAML has something smart called DataBinding. The general concept is you bind a List (for example all strings you want to display in your StackPanel) to an element in the view. Now whenever you modify that list, the view automatically adapts. StackPanel does not support Binding, but for example ListView does (as seen below)
How about you take a look at this for basic informations about databinding: https://blogs.msdn.microsoft.com/jerrynixon/2012/10/12/xaml-binding-basics-101/
With this in mind, you can do something like this:
<!-- insert at the top -->
<Page.Resources>
<DataTemplate x:Name="MyDataTemplate">
<TextBlock Text="{Binding } />
</DataTemplate>
</Page.Resources>
<!-- insert where you want the list to appear -->
<ListView x:Name="ListView" ItemTemplate="{StaticResource MyDataTemplate}" ItemsSource="{Binding MyCollection}" />
The only hard part for you will be to bind the list to the ListView, but I'm sure you can do it with the tutorial from above ;)
Alernatively, you can name your StackPanel with x:Key="MyStack", and add the items manually:
MyStack.Children.Add(new TextBlock() {Text = "myText"});
However, I can really recommend you to do the DataBinding approach, as it makes interacting with the UI so much easier in bigger projects.

How to bind controls to different source from a datatemplate

I have a simple design with several hubsections into which I want to display a ListBox inside a Grid. The ListBox itself will contain two elements : a TextBlock and a TextBox.
The TextBlock in each HubSection will display the same property, so the data binding is quite easy. However, the TextBox should display a different property in each separate HubSection. I am lost and do not even have a clue on how I should proceed.
Here is my XAML code :
<Page.Resources>
<ResourceDictionary>
<DataTemplate x:Key="BaseGridTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="12"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock
Text="{Binding PlayerName}"
FontSize="28"/>
<TextBox Text="{Binding OtherProperty, Mode=TwoWay}"
Background="Lavender" FontSize="28"
Grid.Column="2">
</TextBox>
</Grid>
</DataTemplate>
</ResourceDictionary>
</Page.Resources>
Then in my HubSections, I want to use the above template to display data, like such:
<HubSection Width="350" x:Uid="Test" Header="Test">
<DataTemplate>
<ListBox x:Name="TestListBox" Grid.Column="1"
ItemsSource="{Binding}"
ItemTemplate="{StaticResource BaseGridTemplate}"
>
</ListBox>
</DataTemplate>
</HubSection>
In my UI, both properties (PlayerName and OtherProperty) are displayed correctly. But what I would like to do is to bind to a different property for the TextBox (i.e. different from OtherProperty).
I really have no clue on how I should proceed, or if it even is possible.
I thought I could to something like defining my TextBox in the Resources section like such :
<TextBox Text="{Binding, Mode=TwoWay}">
And then Hope I could add something in the HubSection part ?
Assuming you have a way of knowing when you have to use your different properties I think the best way for you to do this is to use a template selector and have different kind of templates based on what you want to display.
Another alternative would be to use a value converter for TextBox and use the binding you suggested:
<TextBox Text="{Binding, Mode=TwoWay, Converter={StaticResource YourConverter}">
These are just some ideas to get you started, another important thing to know is if are you trying to use a different property on the same object?

Programatically Update XAML Data Template in Windows 8 App

I have a data template like so:
<DataTemplate x:Key="itemTmpl">
<Grid Margin="6">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Height="90" Width="90" Background="#eee" />
<StackPanel Grid.Column="1" VerticalAlignment="Top" Margin="10,0,0,0">
<TextBlock Text="{Binding Id}" Style="{StaticResource TitleTextBlockStyle}" TextWrapping="NoWrap"/>
</StackPanel>
</Grid>
</DataTemplate>
I am setting a List View Item Source in the code behind like so:
lv.ItemTemplate = (DataTemplate)this.Resources["itemTmpl"];
How can I programatically change the Border Background before the template gets rendered to my UI?
You can do a few things:
Parse the Visual Tree (using a VisualTreeHelper or extension method such as from WinRTXamlToolkit), find the border (likely by its x:Name) and set the Background.
Bind the Background to a value in your Item
Bind the Background to a value in your VM
For any of the two Bindings, you can use a Converter to convert from a non-brush value to the proper brush value (such as if you wanted it to change colors based on if it were Checked, a bool, or not).
For the second binding, make sure that you set the Source to your VM, as the DataContext will be, by default, your Item. Also, make sure that your Item inherits from INotifyPropertyChanged so that your Bindings will get updated based on the changes in data properly.

Get element inside a DataTemplate

I have a following XAML and I need to change visibility of imageRemoveButton at runtime from the code behind file. How do I access that button?
<ItemsControl x:Name="ImagesItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="#ffdddddd"
BorderThickness="0,0,0,1">
<Grid Margin="0,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="15" />
<ColumnDefinition />
<ColumnDefinition Width="20" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="1"
VerticalAlignment="Center">
<TextBlock Text="{Binding Name}"
TextWrapping="Wrap" />
</Grid>
<Button Grid.Column="3"
Width="20"
Height="20"
Content="X"
Template="{StaticResource ButtonAddTab}"
Style="{StaticResource ButtonWizard}"
Tag="{Binding}"
x:Name="imageRemoveButton"
Click="ImageRemoveButton_Click" />
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Use the loaded event on the button to save a reference to the button in the cs file. Use this reference when needed to change the visibility.
You cannot reference-by-name elements in a template. There is no matching code-behind/designer property generated by templates.
You want to bind the visibility of a control in the template to a property of your data items. That means you will need to add a property to your data items. Can you provide more details of your data items? If the property is a bool value, use a VisibilityConvertor (dime a dozen on this site).
Think of templates as wanting to always pull settings from the bindings, rather than have settings stuffed into them from the outside.
Try using Source binding to a class that will manage the button's visibility state. Adding a property to all your data items would be painfull but could be required if the button's visibility needs to be set based on a property of your data item. As per standard MVVM patterns you can then control the button's visibility from C#. If you need a specific instance of the button you can add a Loaded event handler to it and cache it accordingly.

How to group controls inside Grid for reuse in XAML?

In my application I have some controls that logically belongs together and is reused many places in different windows. The controls are always placed inside a grid.
Instead of copying the controls (and the code behind) each time I want to use them, I would like to define and maintain them in a single xaml file as a single UserControl.
I have this now:
<Grid>
<Grid.ColumnDefinitions>
[ColumnDefinitions...]
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
[RowDefinitions...]
</Grid.RowDefinitions>
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock Text="Caption" />
<Border Padding="2" x:Name="myBorder">
<TextBox TabIndex="1" x:Name="myTxt"/>
</Border>
</StackPanel>
<ListBox x:Name="myList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Margin="5,50,5,0" Height="100" VerticalAlignment="Top" Visibility="Collapsed" />
[More controls..]
</Grid>
But I want to reuse this part:
<StackPanel Grid.Column="0" Grid.Row="0">
<TextBlock Text="Caption" />
<Border Padding="2" x:Name="myBorder">
<TextBox TabIndex="1" x:Name="myTxt"/>
</Border>
</StackPanel>
<ListBox x:Name="myList" Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Margin="5,50,5,0" Height="100" VerticalAlignment="Top" Visibility="Collapsed" />
as a single control - but how do I define the Grid.Column when using the control (somehow supplying it as a parameter)? - and how do I set the Grid.RowSpan value (eventhough the code is moved to a new xaml file, and not defined inside a grid)?
Any comments?
Make them into a separate usercontrol, then include that in your project.
If you're using Blend, it's really easy, just select all the controls, right click and Make into Usercontrol.
You could also make it into a resource.
Define it in a ResourceDictionary and include the dictionary the places you want to use it.
There is one catch - the resource dictionary returns the same instance everytime - so you have to add the attribute x:Shared="false".
But the wpf way is to figure out how you can do it with a DataTemplate :)

Categories