I'm starting to try to create a modern app with C# and XAML. I've already worked with C# but I never touched in a XAML or WPF piece of code, so I've a beginner question..
I'm using a MSFT template Hub App (XAML) but I don't know how can I set the text value on a TextBlock through the C# code if that textblock is inside the datatemplate.
Is there somebody who can help me with this one?
I already googled for it but I can't get any site with that answer/explanation.
This is an example about what I'm trying to do:
XAML:
<DataTemplate >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Image Source="Assets/MediumGray.png" Stretch="Fill" Width="420" Height="280"/>
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}" Grid.Row="1" Margin="0,10,0,0" TextWrapping="Wrap"
x:Uid="Section1Subtitle" Text="{Binding Score}"/>
<TextBlock x:Name="desc" Grid.Row="2" Margin="0,10,0,0"
x:Uid="DescriptionHeader" Text="{Binding Test}"/>
<TextBlock x:Name="texttest" Grid.Row="3"
Text="{Binding Name}"/>
</Grid>
</DataTemplate>
C# code:
public class Class1
{
string name = "This is a test";
public string Name
{
get { return name; }
set { name = value; }
}
}
What am I doing wrong here?
Thanks in advance,
Problem solved.
Thanks a lot for you help.
Here is the code with the changed which allow me to get the variable from C#:
<DataTemplate>
<Grid>
<Grid.DataContext>
<local:Class1/>
</Grid.DataContext>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Image Source="Assets/MediumGray.png" Stretch="Fill" Width="420" Height="280"/>
<TextBlock Style="{StaticResource SubheaderTextBlockStyle}" Grid.Row="1" Margin="0,10,0,0" TextWrapping="Wrap"
x:Uid="Section1Subtitle" Text="{Binding Score}"/>
<TextBlock x:Name="desc" Grid.Row="2" Margin="0,10,0,0"
x:Uid="DescriptionHeader" Text="{Binding Test}"/>
<TextBlock x:Name="texttest" Grid.Row="3"
Text="{Binding Name}"/>
</Grid>
</DataTemplate>
This one will also walks you through many aspects of the WP development, including using devices, live tiles, etc:
http://www.jeffblankenburg.com/2011/10/31/31-days-of-mango/
It's a bit old though.
Usually there is no way to set something inside of a DataTemplate easily in c# code. However, there are some messy ways to do it through binding, converters, and selectors.
XAML provides a simple and powerful way to auto-update data between the
business model and the user interface. This mechanism is called
DataBinding. Everytime when the data of your business model changes,
it automatically reflects the updates to the user interface and vice
versa. This is the preferred method in WPF to bring data to the user
interface.
Databinding can be unidirectional (source -> target or target <-
source), or bidirectional (source <-> target).
try out the link Learn XAML.
In abstraction, a DataTemplate is a Visual representation of a certain piece of Data.
UI elements inside a DataTemplate should reflect the state of such piece of data.
In XAML-based technologies, DataBinding helps in keeping the UI in sync with the data, using really clean and beautiful declarative means, as opposed to traditional procedural programming.
So, say you have a certain piece of data, like:
public class Person
{
public string FirstName {get;set;}
public string LastName {get;set;}
}
Then you may have a DataTemplate which represents that data, like:
<DataTemplate DataType="local:Person">
<StackPanel>
<TextBlock Text="{Binding LastName}"/>
<TextBlock Text="{Binding FirstName}"/>
</StackPanel>
</DataTemplate>
The key is in the {Binding} declarations.
See the above linked MSDN article for more information.
Related
I have a custom made item template for my listview with which values getting bound in .xaml itself.
I want to change one of the image dynamically in c# side.
I know in c# I can change in ContainerContentChanging but I am not able to access the custom templates image in c# please guide me out of this problem.
Here is my listview
<ListView ItemsSource="{Binding testList}" IsItemClickEnabled="True" ContainerContentChanging="ListView_ContainerContentChanging">
<ListView.ItemTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="2" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.80*"></ColumnDefinition>
<ColumnDefinition Width="0.20*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Margin="0,8,0,8" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center">
<StackPanel Orientation="Horizontal">
<TextBlock FontSize="20" Margin="0,8,0,4" Text="{Binding textVal1}" />
<Image Name="imgStatus" Source="ms-appx:///Assets/Icons/ic_test.png" Height="36" />
</StackPanel>
<TextBlock FontSize="18" Margin="0,0,0,4" Text="{Binding textVal2}" />
<TextBlock FontSize="21" Margin="0,0,0,0" Text="{Binding textVal3}" TextWrapping="WrapWholeWords" MaxLines="2" />
</StackPanel>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I thought lets have the data template in a separate c# file ... but I am not able to link up the c# file inside the grid.
Any help to access the image from the data template is appreciated.
If you define the templates as a static resource, or elsewhere where they can be referenced in XAML, you can use a custom converter with appropriate bindings to handle the change in template.
However, I believe changing the ListView.ItemTemplate will affect all items in the collection. In order to change the template of individual items, you will have to add a wrapper object and modify that object's content (which you can do in a similar manner).
I understand you wished to change the template in C#, but I believe XAML's declarative style should be sufficient.
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.
I am currently binding an object in my code-behind (C#) to my XAML by giving a name to the XAML control, and setting the DataContext in the code-behind.
public partial class SmsControl: UserControl
{
private readonly DataOrganizer _dataOrganizer;
public FunctionalTester _funcTester;
public SmsControl()
{
InitializeComponent();
_dataOrganizer = new DataOrganizer();
_funcTester = new FunctionalTester();
// Set the datacontext appropriately
grpModemInitialization.DataContext = _funcTester;
}
private async void Button_Click_2(object sender, RoutedEventArgs e)
{
await _funcTester.Test();
}
}
And my XAML...
<!-- SMS Test Modem Initialization GroupBox -->
<GroupBox x:Name="grpModemInitialization" Grid.Column="1" Grid.Row="1" Grid.ColumnSpan="3" Style="{StaticResource groupboxViewItem}">
<GroupBox.Header>
<Label Content="SMS Test Modem Initialization" Style="{StaticResource boldHeaderItem}"/>
</GroupBox.Header>
<!-- SMS Test Modem Initialization Grid -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Grid.RowSpan="2" Content="COM:" Style="{StaticResource boldHeaderItem}" />
<ComboBox Grid.Column="1" Grid.Row="0" Grid.RowSpan="2" Style="{StaticResource comboBoxItem}" ItemsSource="{Binding AvailableCommPorts}" SelectedItem="{Binding SelectedCommPort}" />
<Label Grid.Column="2" Grid.Row="0" Content="Modem Ready:" Style="{StaticResource boldHeaderItem}" />
<Label Grid.Column="2" Grid.Row="1" Content="RSSI:" Style="{StaticResource boldHeaderItem}" />
<Label Content="{Binding ModemReady}" Grid.Column="3" Grid.Row="0" HorizontalContentAlignment="Left" VerticalAlignment="Center"/>
<Label Content="{Binding ModemRssi}" Grid.Column="3" Grid.Row="1" HorizontalContentAlignment="Left" VerticalAlignment="Center" />
<Label Grid.Column="4" Grid.Row="0" Content="Modem #:" Style="{StaticResource boldHeaderItem}"/>
<Button Grid.Column="4" Grid.Row="1" Grid.ColumnSpan="2" Content="Initialize" />
<Label Content="{Binding ModemNumber}" Grid.Column="5" Grid.Row="0" HorizontalContentAlignment="Left" VerticalAlignment="Center"/>
</Grid>
</GroupBox>
The code above works fine - no problems. What I'm asking is, if there is a way to set the DataContext of the GroupBox in XAML, referencing my _funcTester object, instead of setting the DataContext in the code-behind? The reason I ask, is because different controls need to be bound to different objects in the code-behind and I'm not finding good resources on how to do so, except as I show above (giving a "x:Name" to each XAML control and setting the DataContext in code-behind). Any help is appreciated! Thanks!
You don't want to reference UI elements by name in the code-behind. Actually any time you can avoid naming an object you save a little in performance. And by setting up your app to use MVVM properly, you gain in performance, readability, maintainability, and code separation.
You want to abstract things further to use the MVVM pattern. You're doing your bindings correctly but consider the pattern. Your view is all correct. Consider adding a class that holds the properties defined currently in your code-behind and the methods called in your event handlers.
public class ViewModel : INotifyPropertyChanged
{
private FunctionalTester _funcTester;
public FunctionalTester FuncTester
{
get
{
return _funcTester;
}
set
{
_funcTester = value;
OnPropertyChanged( "FuncTester" );
}
}
public async void TestAsync( )
{
await _funcTester.Test( );
}
}
A binding to the FuncTester would simply be SomeProperty="{Binding FuncTester}" because the object is set as the DataContext of your view. A decent article that expands on this idea is found here.
Obviously left out some things (like INotifyPropertyChanged implementation and other properties you've defined) for brevity. But just make this class and assign it as your view model. Then the UI (the Xaml and the code-behind) only really deal with the UI and the ViewModel really deals with the data and logic. Great separation. For your event handler, just call ((ViewModel)this.DataContext).Test( ); and you can plug-and-play your ViewModel's to change functionality on the fly. Hope this helps!
Just set the DataContext of whole UserControl to self i.e do
this.DataContext = this; in constructor.
Then define the Property for _functinTester
public FunctionalTester FuncTester { get {return _funcTester} };
Now in your xaml you can do
<GroupBox x:Name="grpModemInitialization" DataContext="{Binding FuncTester}"/>
In this way since you have the DataContext set for your whole usercontrol, you can bind any control to any property within that DataContext
This is a problem I've been trying to solve for a couple of days but I'm yet to find a good solution.
I have a main WPF window which contains a StackPanel. I also have a class called "MessageManagement" which contains a List of custom WPF controls called "MessagePreview".
MessagePreview
<UserControl x:Class="FinalYearProject.MessagePreview"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="58" d:DesignWidth="254" Background="White" BorderThickness="1" BorderBrush="#FF320000">
<Grid Name="grid1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50" MaxWidth="50" MinWidth="50" />
<ColumnDefinition Width="197*" MinWidth="20" />
</Grid.ColumnDefinitions>
<Grid Grid.Column="1" Name="grid2">
<Grid.RowDefinitions>
<RowDefinition Height="31*" />
<RowDefinition Height="34*" />
<RowDefinition Height="33*" />
</Grid.RowDefinitions>
<Label Content="-ERROR-" Height="28" HorizontalAlignment="Left" Margin="2,1,0,0" Name="senderLabel" VerticalAlignment="Top" Foreground="#FF0000B4" Grid.RowSpan="2" />
<Label Content="-ERROR-" Height="33" HorizontalAlignment="Left" Name="previewLabel" VerticalAlignment="Top" Margin="-1,17,0,0" Grid.RowSpan="3" />
<Label Content="-Error-" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="0,16,6,0" Name="timeLabel" VerticalAlignment="Top" Grid.RowSpan="2" Width="196" />
</Grid>
<Grid Name="grid3">
<Grid.RowDefinitions>
<RowDefinition Height="29*" />
<RowDefinition Height="27*" />
</Grid.RowDefinitions>
<Image Height="38" HorizontalAlignment="Center" Margin="3,0,6,20" Name="image1" Stretch="Fill" VerticalAlignment="Bottom" Width="41" Source="/FinalYearProject;component/Images/twitter-bird-light-bgs.png" Grid.RowSpan="2" />
<CheckBox Height="16" Margin="15,0,22,6" Name="checkBox" VerticalAlignment="Bottom" Grid.Row="1" IsChecked="False" />
</Grid>
</Grid>
MessageManagement
List<MessagePreview> unread = new List<MessagePreview>();
public bool messagesLocked()
{
foreach (MessagePreview m in unread)
{
if ((bool)m.checkBox.IsChecked)
return true;
}
return false;
}
MainWindow C#
MessageManagement messageManagement;
MessagesPanel1 is a blank StackPanel inside MainWindow
if (!messageManagement.messagesLocked())
{
foreach (Message m in messageManagement.getListOfMessages()
{
MessagesPanel1.Children.Add(m)
}
}
When you add the custom control to the StackPanel it creates a duplicate of the original, which means changing a value on the controls in the StackPanel does not affect the controls in my MessageManagement List.
Is there a way that I can reference my original controls so that they change together? I looked into DataBinding but I'm not sure if that's the right area.
Again, You do not make a List of UI elements in WPF. You use an ItemsControl and set its ItemTemplate Property to whatever UI you need to represent your items:
<ItemsControl ItemsSource="{Binding Messages}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<!-- Everything you placed in your usercontrol, or else an instance of your usercontrol itself -->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If saying "change" you mead a data, inside WPF you never and ever need to access UI elements directly, as they are subject to change. What you have to do is to read the data bound to them. With correct databinding setupped on that controls you will have always updated information.
Accessing UI elements is never easy, but except that, it's completely unreliable way to access your data, as the way control is injected into the VisualTree may be changed, so your code will fail on next update.
For example on how to architect that can have a look here:
WPF Data Binding Examples
I want to develope my first real WPF Desktop Application. It's quite a lot of time since my last desktop application, which was developed with .NET 2.0 and Windows Forms. Since i have to develope a new Desktop Application, and i can take the advantage of .NET 4.0 i would really like to use WPF.
I am reading some online tutorial and documentation to choose the right way to do it, but i am a little lost with the new controls.
Basically i need a panel which show some textboxes and label above a datagrid.
Do you think that i can achieve such a result with gridpanel control ?
Thank you.
In WPF you usually use several different controls. Control composition in WPF is a major feature and you'll gain a lot by knowing it and taking advantage of it.
When looking at your screen i divide it in two major parts:
- campi di ricerca
- resultati
For these two i'd use a Grid with two rows and one column.
The ricerca area could use a DockPanel with the header aligned at the top and aother Grid With Fill contents.
... and so on, so on.
My best advice is for you to learn about WPF Composition
Hope i've helped a little bit
Yes, you need to use a Grid, with multiple rows and columns.
See the example on MSDN
<Grid VerticalAlignment="Top" HorizontalAlignment="Left" ShowGridLines="True" Width="250" Height="100">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock FontSize="20" FontWeight="Bold" Grid.ColumnSpan="3" Grid.Row="0">2005 Products Shipped</TextBlock>
<TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="0">Quarter 1</TextBlock>
<TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="1">Quarter 2</TextBlock>
<TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="2">Quarter 3</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0">50000</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="1">100000</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="2">150000</TextBlock>
<TextBlock FontSize="16" FontWeight="Bold" Grid.ColumnSpan="3" Grid.Row="3">Total Units: 300000</TextBlock>
</Grid>