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
Related
Firstly a Disclaimer: I have read dozens of posts with hundreds of answers and yet I have not found a solution. I have attempted to resolve this every which way I can find and yet the UI does not update.
I am writing a UWP app as a personal project. In this particular scenario I have a Grid with an assigned DataContext with a series of TextBlocks which are Bound to Properties. I am using "Binding" and not "x:Bind" due to the Grid been part of a ControlTemplate.
I have implemented the Properties with INotifyPropertyChanged.
I have backing fields.
I have stepped through the debugging and the Properties are updated and retain the values assigned.
I have tried using UpdateSourceTrigger=PropertyChanged.
I have tried different Modes - OneWay/TwoWay etc.
The issue: The UI completely ignores the changes and NEVER updates.
I have set the properties with a value within the Page Constructor, and that is set, but no changes thereafter are applied... EVER.
Please see the relevant code snippets below and maybe someone can see something I have not.
Thanks in advance.
Grid XAML:
<Grid
Grid.Row="1"
BorderBrush="{StaticResource SystemControlChromeLowAcrylicElementBrush}"
BorderThickness="2,0,2,2">
<Grid.DataContext>
<views:MenusPage />
</Grid.DataContext>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="152" />
<ColumnDefinition Width="152" />
<ColumnDefinition Width="152" />
<ColumnDefinition Width="152" />
<ColumnDefinition Width="152" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="0"
Grid.Column="0"
Grid.ColumnSpan="3"
Padding="15,0,0,0"
HorizontalAlignment="Left"
VerticalAlignment="Center"
FontWeight="Bold"
Text="Totals" />
<Border
Grid.Row="0"
Grid.Column="3"
Margin="0,2,0,-2"
BorderBrush="{StaticResource SystemControlChromeLowAcrylicElementBrush}"
BorderThickness="2,0,0,0">
<TextBlock
Padding="10,0,0,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding CaloriesTotal, Mode=OneWay}" />
</Border>
<Border
Grid.Row="0"
Grid.Column="4"
Margin="0,2,0,-2"
BorderBrush="{StaticResource SystemControlChromeLowAcrylicElementBrush}"
BorderThickness="2,0,0,0">
<TextBlock
Padding="10,0,0,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding ProteinTotal}" />
</Border>
<Border
Grid.Row="0"
Grid.Column="5"
Margin="0,2,0,-2"
BorderBrush="{StaticResource SystemControlChromeLowAcrylicElementBrush}"
BorderThickness="2,0,0,0">
<TextBlock
Padding="10,0,0,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding FatTotal}" />
</Border>
<Border
Grid.Row="0"
Grid.Column="6"
Margin="0,2,0,-2"
BorderBrush="{StaticResource SystemControlChromeLowAcrylicElementBrush}"
BorderThickness="2,0,0,0">
<TextBlock
Padding="10,0,0,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding CarbTotal}" />
</Border>
<Border
Grid.Row="0"
Grid.Column="7"
Margin="0,2,0,-2"
BorderBrush="{StaticResource SystemControlChromeLowAcrylicElementBrush}"
BorderThickness="2,0,0,0">
<TextBlock
Padding="15,0,0,0"
VerticalAlignment="Center"
FontWeight="Bold"
Text="{Binding SugarTotal}" />
</Border>
</Grid>
Properties:
private double _caloriesTotal;
public double CaloriesTotal
{
get { return _caloriesTotal; }
set { Set(ref _caloriesTotal, value); }
}
private double _proteinTotal;
public double ProteinTotal
{
get { return _proteinTotal; }
set { Set(ref _proteinTotal, value); }
}
private double _fatTotal;
public double FatTotal
{
get { return _fatTotal; }
set { Set(ref _fatTotal, value); }
}
private double _carbTotal;
public double CarbTotal
{
get { return _carbTotal; }
set { Set(ref _carbTotal, value); }
}
private double _sugarTotal;
public double SugarTotal
{
get { return _sugarTotal; }
set { Set(ref _sugarTotal, value); }
}
PropertyChanged:
public event PropertyChangedEventHandler PropertyChanged;
private void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (Equals(storage, value))
{
return;
}
storage = value;
OnPropertyChanged(propertyName);
}
private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
Code Setting the values:
(NOTE: this is the shortened version but the same effect applies, setting the values in the constructor applies the values and UI updates but setting the values anywhere else on the page, within any method that is executed, they are ignored and the UI shows zeros.)
CaloriesTotal = 10;
ProteinTotal = 20;
FatTotal = 30;
CarbTotal = 40;
SugarTotal = 50;
I found the solution, and while I am dumbfounded as to how it actually worked while the other 'normal' solutions that should have worked did not, is beyond me.
Anyway thanks to #jsmyth886 and #Alexey for your help and suggestions in troubleshooting.
Since posting this question I have tried separating out the properties into another class, referencing <Models:MenuTypesModel x:Key="MenuTotals" /> within the <Page.Resources> block -- failed
I Tried setting the DataContext of the Page itself -- failed
The <Grid> ... </Grid> XAML in my question is, as said, part of a ControlTemplate I even moved the block out of the Control Template and onto the Page itself to eliminate any clashing with other datasources - mind you all different naming structures and paths, so ultimately should not have clashed -- this did not work either, so -- failed
It was #Alexey response that got me thinking in a different line and my searching ultimately led me to this post Binding to Self/'this' in XAML which solved my problem.
I removed the following datacontext from the Grid:
<Grid.DataContext>
<views:MenusPage />
</Grid.DataContext>
And replaced it with this DataContext="{Binding ElementName=_this}" and made sure of my textblock bindings, the end result (Very Short Version):
<Grid ... DataContext="{Binding ElementName=_this}">
...
<TextBlock ... Text="{Binding CaloriesTotal}" />
...
</Grid>
And finally added the a Name attribute to the Page itself:
x:Name="_this"
And it worked!!!!
I have changes nothing in the way I have set my properties.
My properties are still defined as per the question, I have no viewmodels or additional classes, nor am I setting DataContext in the CodeBehind.
Simply adding DataContext="{Binding ElementName=_this}"the grid and x:Name="_this" to the Page allows the UI to reflect the changes to the properties every time.
An oddly simple solution.
A NOTE: though, while I was trying different things though, when I had the Grid on the page itself outside of the ContentTemplate, I set the DataContext of the page in codebehind DataContext = this; and it started populating but still failed to affect the Grid in the Control template.
So unless there is a clash that I didn't see or because I have other DataSources which are unrelated as well as assigned directly the to control that needs it... I cannot figure why it works now with this simple binding.
Anyway, thanks for the assistance and I hope this helps someone else in the future.
If you want to benefit from NavigationCacheMode (Enabled/Required) for the sake of performance, you have to update the bindings on data context change.
public MainPage()
{
this.InitializeComponent();
this.DataContextChanged += (s, e) => this.Bindings.Update();
}
Please use binding DataContext for your main object, not for DataGrid:
<Window.DataContext>
<views:MenusPage />
</Window.DataContext>
So I have a ListView bound to a Client Object. The client properties are shown on some textbox next to the ListView. If I change the name (that is shown in the ListView), I can't select anything else unless I revert back the name.
The ListView SelectedItem always stays selected if I change the value in the Text Box and the Text Box all show the same values, even if I select another.
I tried clearing the selected item, the ItemsSource nothing will work. It must be something with the source and the bindings.
My Source is a ObservableDictionary that I found somewhere here, so maybe that is the issue. My Client Object is Also implementing INotifyPropertyChanged. I tried a lot of thing with Binding and etc. But whatever I try the text Box will stay like that even if I select something else as long has the value isn't the restored to it's Original Value I can't select anything else.
XAML
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="10"/>
</Grid.ColumnDefinitions>
<ListView x:Name="listView" Grid.RowSpan="7" Grid.Row="1" Margin="5,0" SelectionMode="Single" IsSynchronizedWithCurrentItem="True">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ClientIDandName}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<TextBlock Text="ID" Grid.Row="1" Grid.Column="1"/>
<TextBox Text="{Binding SelectedItem.ClientID, ElementName=listView}" IsEnabled="False" Grid.Row="1" Grid.Column="2"/>
<TextBlock Text="Nom" Grid.Row="2" Grid.Column="1"/>
<TextBox Text="{Binding SelectedItem.ClientName, ElementName=listView}" Grid.Row="2" Grid.Column="2"/>
<TextBlock Text="Temps de Transit" Grid.Row="3" Grid.Column="1"/>
<TextBox Text="{Binding SelectedItem.TransitTime, ElementName=listView}" Grid.Row="3" Grid.Column="2"/>
<TextBlock Text="Nbr. de Jour Max" Grid.Row="4" Grid.Column="1"/>
<TextBox Text="{Binding SelectedItem.MaxShipDays, ElementName=listView}" Grid.Row="4" Grid.Column="2"/>
<TextBlock Text="Exclure" Grid.Row="5" Grid.Column="1"/>
<CheckBox IsChecked="{Binding SelectedItem.IsExcluded, ElementName=listView}" Grid.Row="5" Grid.Column="2" VerticalAlignment="Bottom"/>
<Button x:Name="btnSave" Content="Sauvegarder" Click="btnSave_Click" Grid.Row="7" Grid.Column="1" Grid.ColumnSpan="2" HorizontalAlignment="Center"/>
</Grid>
Code Behind
public partial class ClientConfig : UserControl
{
public ClientConfig()
{
InitializeComponent();
listView.ItemsSource = Client.LocalDB.Values.ToObservableCollection();
}
private void btnSave_Click(object sender, RoutedEventArgs e)
{
Client client = listView.SelectedItem as Client;
if (client != null)
{
client.Save();
}
}
}
I usually use the MVVM design pattern and keep my objects in a viewmodel, so you will have to modify the code accordingly.
I think that your problem is how you create your ObservableCollection in code. When you call ToObservableCollection(), it enumerates all of the values in Client.LocalDB.Values, but I don't think it will persist as an object like you expect it to - especially since it is local to the method that you are creating it in. The ListView continues to show data because nothing is telling it to change, but the ObservableCollection does not actually exist anymore. You will want to create an observable collection that is global to the whole class, and populate it instead.
This is untested code, but should get you close... in code behind:
private ObservableCollection<Whatever> _myCollection;
public ObservableCollection<Whatever> MyCollection
{
get
{
return _myCollection;
}
set
{
if (_myCollection != value)
{
_myCollection = value;
RaisePropertyChanged("MyCollection");
}
}
}
private Whatever _selectedWhatever;
public Whatever SelectedWhatever
{
get
{
return _selectedWhatever;
}
set
{
if (_selectedWhatever != value)
{
_selectedWhatever = value;
RaisePropertyChanged("SelectedWhatever");
}
}
}
public ClientConfig()
{
InitializeComponent();
foreach (Whatever whatevs in Client.LocalDB.Values.ToObservableCollection())
{
MyCollection.Add(whatevs); // populate the collection this way and it will persist
}
}
and in XAML:
<ListView x:Name="listView" Grid.RowSpan="7" Grid.Row="1" Margin="5,0" SelectionMode="Single" IsSynchronizedWithCurrentItem="True" ItemSource="{Binding MyCollection}" SelectedItem="{Binding SelectedWhatever}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ClientIDandName}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Notice that you have now bound the collection to a persistent variable that can be changed, and you have bound the selected item to a specific instance of that collection. Now you can bind your textboxes to the SelectedWhatever and changes made in one place should be reflected in the other place.
I have a pretty straight-forward MVVM Master\Detail window -
XAML snippet:
<Grid Grid.Row="0" Grid.Column="0" >
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Label Content="ID:" Grid.Row="0" Grid.Column="0"/>
<Label Content="{Binding SelectedCustomer.CustId}" Grid.Row="0" Grid.Column="1"/>
<Label Content="Name:" Grid.Row="1" Grid.Column="0"/>
<TextBox Text="{Binding SelectedCustomer.Name}" Grid.Row="1" Grid.Column="1"/>
<Label Content="Address:" Grid.Row="2" Grid.Column="0"/>
<TextBox Text="{Binding SelectedCustomer.Address}" Grid.Row="2" Grid.Column="1"/>
<Label Content="City:" Grid.Row="3" Grid.Column="0"/>
<TextBox Text="{Binding SelectedCustomer.City}" Grid.Row="3" Grid.Column="1"/>
<Label Content="State:" Grid.Row="4" Grid.Column="0"/>
<TextBox Text="{Binding SelectedCustomer.State}" Grid.Row="4" Grid.Column="1"/>
<Label Content="ZIP:" Grid.Row="5" Grid.Column="0"/>
<TextBox Text="{Binding SelectedCustomer.ZIP}" Grid.Row="5" Grid.Column="1"/>
</Grid>
<Grid Grid.Row="1" Grid.Column="0">
<DataGrid ItemsSource="{Binding CustomerCollection}" SelectedItem="{Binding SelectedCustomer}"></DataGrid>
</Grid>
</Grid>
Nothing really fancy in the Model:
public ObservableCollection<Customer> CustomerCollection { get; set; }
private Customer _selectedCustomer;
public Customer SelectedCustomer
{
get
{
return _selectedCustomer;
}
set
{
_selectedCustomer = value;
OnPropertyChanged("SelectedCustomer");
}
}
I can select a Master row and the detail will fill in appropriately. That part works great.
My problem comes when I edit the Detail, it changes the Master before the user hits Save. If I change one of the properties and it looses focus the databinding will set the Master row to be the same as the new information. I have tried various versions of Mode=OneWay and they don't help.
How do I make the databinding for the SelectedItem only go from the datagrid out, not back in? I will refresh the datagrid when the user clicks Save, that's not an issue. It's more important to me that the record not change mid-stream than to refresh the grid too often.
You have to set the OneTime binding on the properties of the collection, not the collection itself.
Try disabling autogeneratecolumns on your grid and define the columns yourself with a onetime binding. That should get the behavior you need.
Something like this (not tested):
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Customers}" SelectedItem="
{Binding SelectedCustomer}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name, Mode=OneTime}" />
<DataGridTextColumn Binding="{Binding Address, Mode=OneTime}" />
</DataGrid.Columns>
</DataGrid>
If you really want to separate detail from master though, i recommend having a separate selectedCustomer property and copy that one to the collection bound to the grid when you are done editing. The onetime binding is nice, but you have to update your grid manually, which takes a bit of code if you want to work with strict MVVM. Also note that when you edit the detail, the SelectedCustomer will still be updated on your collection in the ViewModel, the grid just won't reflect the changes yet. So if you hit save the latest information would still be written to the DB.
You can use UpdateSourceTrigger
But in this case you have to run thru all controls in detail form and manually update the source.
More complex solution is you can load selected row in to detail form not from grid but from server again.
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.
I'm quite new to xaml. I have looked around quite a bit and I'm finding posts that come close to what I'm trying to do, but I haven't been able to find exactly what I'm looking for.
I have an external WPF UserControl with Dependency Properties. In another project, I've imported the dll from my UserControl library. I created a static grid in my project in which there is an instance of my usercontrol in each cell. Each UserControl DependencyProperty I've defined is available in xaml.
At runtime, I will fill a DataTable with data then I need to bind the UserControls in my grid to the DataTable, so that each UserControl reflects values from the DataTable. Each row in the DataTable will have several column values that I need to bind to my UserControl's Dependency Properties.
What I'm having trouble with is defining the DataContext after the data is retrieved in such a way that I can define a binding path for the Dependency properties in each one of my UserControls. If I set the DataContext of my grid to the DataTable like this:
Grid.DataContext = myDataTable;
Then what is the syntax for the binding path of the UserControl property?
<my:ucControl Name="myUserControl1" MyDependencyProp="{Binding Path=??}" Grid.Column="1" Grid.Row="1" />
I've seen posts where some have used "Row[0][column_name]" or variations of that, but I can't seem to get anything like that working. Am I approaching the binding method wrong?
Here is my UserControl code:
public partial class ucControl : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public static DependencyProperty IndexProperty =
DependencyProperty.Register("Index", typeof(int), typeof(ucControl));
public static DependencyProperty StatusProperty =
DependencyProperty.Register("Status", typeof(String), typeof(ucControl),
new PropertyMetadata(string.Empty, new PropertyChangedCallback(NotifyPropertyChanged)));
#region PublicProperties
public int Index
{
get { return (int)GetValue(IndexProperty); }
set { SetValue(IndexProperty, value); }
}
public String Status
{
get { return (int)GetValue(StatusProperty); }
set { SetValue(StatusProperty, value); }
}
#endregion
}
Here is the XAML:
<Grid Canvas.Left="17" Canvas.Top="46" Name="MasterGrid" Width="1259" HorizontalAlignment="Left" VerticalAlignment="Top">
<Grid.RowDefinitions>
<RowDefinition Name="ColumnHeader" Height="25" />
<RowDefinition Height="40" />
<RowDefinition Height="40" />
<RowDefinition Height="40" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Name="DateColumn" Width="80"/>
<ColumnDefinition Width="65"/>
<ColumnDefinition Width="65"/>
<ColumnDefinition Width="65"/>
</Grid.ColumnDefinitions>
<my:ucControl Name="myControl1" Index="{Binding ??}" Status="{Binding ??}" Grid.Column="1" Grid.Row="1" Grid.RowSpan="1" />
<my:ucControl Name="myControl2" Index="{Binding ??}" Status="{Binding ??}" Grid.Column="1" Grid.Row="2" Grid.RowSpan="1" />
<my:ucControl Name="myControl3" Index="{Binding ??}" Status="{Binding ??}" Grid.Column="1" Grid.Row="2" Grid.RowSpan="1" />
</Grid>
Here is my C#:
String strViewSelect = "SELECT index, status, FROM Table1 ORDER BY index ASC;
System.Data.DataSet dsMonthData = dataClass.mFillDataset(strViewSelect);
MasterGrid.DataContext = dsMonthData;
UPDATED: Here's how I've solved this issue:
First, I'm creating a static DataSource in my project that contains the columns that I want to bind to. Adding arguments to the query so I can specify the data that I want to retrieve.
The following was added to the XAML in my window:
<Window.Resources>
<my1:myDataSet x:Key="myDataSet" />
<CollectionViewSource x:Key="tableNameViewSource" Source="{Binding Path=tableName, Source={StaticResource myDataSet}}" />
</Window.Resources>
Next, I've added the following XAML to my grid:
DataContext="{StaticResource tableNameViewSource}"
Now, I can add the bindings to my UserControl properties:
Status="{Binding Path=statusColumn}"
<my:ucCalendarCell Name="ucControl1" Status="{Binding Path=statusColumnName}" Index="{Binding Path=indexColumnName}" Grid.Column="1" Grid.Row="1" Grid.RowSpan="1" />
A little bit of work left to tailor to my needs, but that's the basic idea. I'm positive that is more than one way to do this, but this should work for me.