I'm trying to bind to an instance method of an object but I can only find examples to bind to properties or static methods. Here's the relevant part of my code:
<Window.Resources>
<ObjectDataProvider x:Key="identifier" MethodName="getIdentifier" ObjectType="{x:Type self:PartModel}" />
</Window.Resources>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid Margin="0,0,0,5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20" />
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding identifier}" Grid.Column="0" />
<TextBlock Text="{Binding Title}" Grid.Column="1" />
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
PartModel is an object that I use for filling the ItemsControl. The Title shows up and the getIdentifier method works when I call it in my regular code. But in my View only the title shows and the column for the identifier remains empty.
Is this even possible or do I have to write the identifier to a property of the model?
You are currently binding to an identifier property, which does not exist on the DataContext object of your list items.
Set the binding Source object instead, to the ObjectDataProvider resource that is references by the identifier resource key:
<TextBlock Text="{Binding Source={StaticResource identifier}}"/>
Related
Referencing this example:
https://learn.microsoft.com/en-us/dotnet/framework/wpf/data/how-to-bind-to-a-collection-and-display-information-based-on-selection
(some relevant code snippets:)
<Window.Resources>
<local:People x:Key="MyFriends"></local:People>
<DataTemplate x:Key="DetailTemplate">
<Border Width="300" Height="100" Margin="20"
BorderBrush="Aqua" BorderThickness="1" Padding="8">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="First Name:"/>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=FirstName}"/>
<TextBlock Grid.Row="1" Grid.Column="0" Text="Last Name:"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=LastName}"/>
<TextBlock Grid.Row="2" Grid.Column="0" Text="Home Town:"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=HomeTown}"/>
</Grid>
</Border>
</DataTemplate>
</Window.Resources>
<ListBox Width="200" IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Source={StaticResource MyFriends}}"/>
<ContentControl x:Name="contentControl1"
Content="{Binding Source={StaticResource MyFriends}}"
ContentTemplate="{StaticResource DetailTemplate}" />
Both ListBox.ItemsSource and ContentControl.Content bind to the same source (MyFriends, an instance of the People class which derives from ObservableCollection<Person>). If my understanding is correct, this means that both the ListBox.ItemsSource and ContentControl.Content properties will be bound to the same implicitly created instance of ListCollectionView.
I understand that setting ListBox.IsSynchronizedWithCurrentItem="True" synchronizes ListBox.SelectedItem and ItemCollection.CurrentItem.
DetailTemplate (above) displays the details of the selected ListBox item, despite being 'bound' to a ListCollectionView. Specifying Path=/ (what I thought would be necessary to achieve the resulting behavior) does not have any effect - it's as if WPF knows to do it implicitly somehow:
<ContentControl x:Name="contentControl1"
Content="{Binding Source={StaticResource MyFriends}, Path=/}"
ContentTemplate="{StaticResource DetailTemplate}" />
As a test, I created another ContentControl with Content bound to a DataTemplate containing a ListBox:
<ContentControl x:Name="contentControl2"
Content="{Binding Source={StaticResource MyFriends}}"
ContentTemplate="{StaticResource DetailTemplate2}" />
<DataTemplate x:Key="DetailTemplate2">
<ListBox ItemsSource="{Binding}"></>
</DataTemplate>
And it displayed the list.
My question is: Why does DataTemplate get the selected Person object while the ListBox and DetailTemplate2 get the People collection?
(the behavior is desirable, I just don't understand what black magic is occurring under the hood to make it so)
Is a good question! I didn't notice that until read your post. So, after did some digging from source code of PropertyPathWorker, it appears that when PropertyPathWorker failed to solve a member of an object, in your case, it try to solve 'FirstName', 'LastName' ect. with 'MyFriends', it will try to solve it with the view of the object. And if still failed, it will try to solve it with view's CurrentItem, and that's where the magic happened. You can find those codes in PropertyPathWorker.UpdateSourceValueState(int k, ICollectionView collectionView, object newValue, bool isASubPropertyChange) and PropertyPathWorker.ReplaceItem(int k, object newO, object parent).
I am currently building a Windows Phone Applicaation, based off of the HubAppTemplate.
The template comes with a sample .JSON data source that it uses to populate the data of each HubSection. However, I want to use a non JSON type of data as the basis of my code. Inside my C# code, I need to make a function call to my backend to get the type of data I want out of it.
I can put this data inside of my own custom list (on the C# side), but how can I make that list act as the data source for my HubSection? Any old listview/list box works perfectly. Basically, I need help wiring the C# to the XAML -- the main issue is that I cannot access my listView inside of the datatemplate by name.
Can anyone give me some pointers to get going in the right direction?
Here is some reference code to show you what I am talking about:
<HubSection x:Uid="Clubs" Header="Clubs" DataContext="{Binding Groups}" HeaderTemplate="{ThemeResource HubSectionHeaderTemplate}">
<DataTemplate>
<ListView Name="ClubsList"
IsItemClickEnabled="True"
ItemsSource="{Binding}"
ItemClick="GroupSection_ItemClick"
ContinuumNavigationTransitionInfo.ExitElementContainer="True">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,27.5">
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemTextBlockStyle}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</HubSection>
The above XAML is basically pulled straight from the hubapp template. I want to be able to use my own itemssource inside of that ListView that is generated from my C# code -- however, I cannot figure out how this ItemsSource works. I also cannot access my listview by name (ClubsList).
Here is the initialization code going on up top (wasn't sure if it was important to post this or not):
<Page
x:Class="HubAppTemplate.HubPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:HubAppTemplate"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:data="using:HubAppTemplate.Data"
DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
d:DataContext="{Binding Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="HubSectionHeaderTemplate">
<TextBlock Margin="0,0,0,-9.5" Text="{Binding}"/>
</DataTemplate>
<!-- Grid-appropriate item template as seen in section 2 -->
<DataTemplate x:Key="Standard200x180TileItemTemplate">
<Grid Margin="0,0,9.5,9.5" Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" Height="138.5" Width="138.5"/>
<TextBlock Text="{Binding Title}" VerticalAlignment="Bottom" Margin="9.5,0,0,6.5" Style="{ThemeResource BaseTextBlockStyle}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="StandardTripleLineItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,9.5,0,0" Grid.Column="0" HorizontalAlignment="Left">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" Height="79" Width="79"/>
</Border>
<StackPanel Grid.Column="1" Margin="14.5,0,0,0">
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemTextBlockStyle}"/>
<TextBlock Text="{Binding Description}" Style="{ThemeResource ListViewItemContentTextBlockStyle}" Foreground="{ThemeResource PhoneMidBrush}" />
<TextBlock Text="{Binding Subtitle}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}" />
</StackPanel>
</Grid>
</DataTemplate>
<DataTemplate x:Key="StandardDoubleLineItemTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Background="{ThemeResource ListViewItemPlaceholderBackgroundThemeBrush}" Margin="0,9.5,0,0" Grid.Column="0" HorizontalAlignment="Left">
<Image Source="{Binding ImagePath}" Stretch="UniformToFill" AutomationProperties.Name="{Binding Title}" Height="79" Width="79"/>
</Border>
<StackPanel Grid.Column="1" Margin="14.5,0,0,0">
<TextBlock Text="{Binding Title}" Style="{ThemeResource ListViewItemTextBlockStyle}"/>
<TextBlock Text="{Binding Subtitle}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}"/>
</StackPanel>
</Grid>
</DataTemplate>
</Page.Resources>
<Grid x:Name="LayoutRoot">
<Hub x:Name="Hub" x:Uid="Hub" Header="Club Alert" Background="{ThemeResource HubBackgroundImageBrush}">
It is pulling from the JSON backend, but I want to just use my own custom listview for each section. Deleting the DataSource and data template headers gives me errors, however.
Thank you so much for your help in advance!
--A total newbie
HubSection elements require their contents to be populated via a template, so you can't just remove the <DataTemplate> tags, unfortunately. However, there is a simple way to accomplish what you are trying to do, if I understand you correctly.
If you're starting with the default Hub template, you should have this function in your HubPage.xaml.cs file
private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
// TODO: Create an appropriate data model for your problem domain to replace the sample data
var sampleDataGroups = await SampleDataSource.GetGroupsAsync();
this.DefaultViewModel["Groups"] = sampleDataGroups;
MainViewModel viewModel = DataContext as MainViewModel;
if (!viewModel.IsDataLoaded)
{
viewModel.Load();
}
}
this.DefaultViewModel is just a Dictionary, and they have loaded the sample JSON into a variable and stored this in the ["Groups"] key of the dictionary. Since the Page's DataContext is being bound to {Binding DefaultViewModel, RelativeSource={RelativeSource Self}}, the HubSection's DataContext is being bound to {Binding Groups}, and the ItemsSource of the ListView in each DataTemplate is being bound to {Binding}, each element of the loaded JSON is being used to fill the items of the ListView.
A simple solution would be to assign this.DefaultViewModel["Groups"] to the C# List you are creating from the data you load from your back end.
Something like this:
private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
// TODO: Create an appropriate data model for your problem domain to replace the sample data
var myData = await GetListOfThingsFromBackend();
this.DefaultViewModel["Groups"] = myData;
MainViewModel viewModel = DataContext as MainViewModel;
if (!viewModel.IsDataLoaded)
{
viewModel.Load();
}
}
A better approach would probably be to separate out all ViewModel functionality to it's own class that is better suited to your needs, and then adjust the various DataContext properties throughout the XAML, but that would likely take more time. I can elaborate if needed, but the simple solution is probably enough for now.
I want to create a debug window that will allow us to edit properties from various objects in our app while it runs. This will allow us, for example, to tweak threshold values for certain heuristic rules in our app, without requiring a rebuild and/or app restart.
The goal is to tell the debug window to enable editing some property of an object. The window then obtains the property's value, keeps a weak reference to the object and displays an appropriate data template (based on the value's type) to enable us to edit the value and apply the new value to the object when needed.
Problem:
The data templates are applied correctly and the value is displayed for each debug item in a TextBox. However, the Value property of each DebugItem, to which the TextBox is bound, is NEVER updated. I have set a breakpoint on that property's setter; the breakpoint is never triggered.
Here is my current setup:
I have a DebugItems collection of DebugItem objects in my view model.
Each DebugItem has a Value property of type object.
For debugging purposes, the Value property always contains a string.
I have created a data template for the DebugItem type and the System:String type.
My window contains a ListBox that is bound to the DebugItems collection and displays DebugItems using the data template defined above, in a ContentPresenter. A TextBox inside that data template is also bound to the Value so that it enables us to edit the string value using the other, System:String data template defined above *.
* I am under the impression that this has to do with why the edit doesn't work. I could be mistaken, through.
Relevant part of the window:
<Grid Background="#CECECE">
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<ItemsControl ItemsSource="{Binding DebugItems}" Background="Transparent" BorderBrush="Transparent" />
</ScrollViewer>
</Grid>
My data template:
(Of particular interest are the inner ContentPresenter and its embedded System:String data template.)
<DataTemplate DataType="{x:Type Debug:DebugItem}">
<Grid Height="60" d:DesignWidth="403.06">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="140" />
<ColumnDefinition Width="181*" />
<ColumnDefinition Width="110" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Label}" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="14,0,0,0" Foreground="Black" FontWeight="Bold" />
<ContentPresenter VerticalAlignment="Center" Grid.Column="1" Content="{Binding Value}" Height="Auto">
<ContentPresenter.Resources>
<!-- String -->
<DataTemplate DataType="{x:Type System:String}">
<TextBox Text="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</ContentPresenter.Resources>
</ContentPresenter>
<UniformGrid Grid.Column="2" Rows="1">
<Button Margin="8,8,0.5,8" Command="{Binding UpdateCommand}" Style="{DynamicResource ButtonStyle}" Content="Update" />
<Button Margin="4.5,8,8,8" Command="{Binding ApplyCommand}" Style="{DynamicResource ButtonStyle}" Content="Apply" />
</UniformGrid>
</Grid>
</Grid>
</DataTemplate>
Any ideas?
I have a WPF ListBox where I have checkboxes, but what's the way to get the list of items that are checked?
The ListBox is data binded to a Dictionary<T>.
Here is the XAML:
<Window x:Class="WpfApplication.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1"
Height="300"
Width="300">
<Grid Margin="10">
<ListBox ItemsSource="{DynamicResource Nodes}" Grid.IsSharedSizeScope="True" x:Name="MyList">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Key" />
<ColumnDefinition SharedSizeGroup="Name" />
<ColumnDefinition SharedSizeGroup="Id" />
</Grid.ColumnDefinitions>
<CheckBox Name="NodeItem" Click="OnItemChecked">
<StackPanel Orientation="Horizontal">
<TextBlock Margin="2" Text="{Binding Value.Name}" Grid.Column="1"/>
<TextBlock Margin="2" Text="-" Grid.Column="2"/>
<TextBlock Margin="2" Text="{Binding Value.Id}" Grid.Column="3"/>
</StackPanel>
</CheckBox>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</Window>
This is usually done through a ViewModel, that is a data structure that exposes to the view (through the DataContext) both the model (your data) and view-specific information, like whether an item is checked or not.
In your example, your Dictionary would not be, say, a Dictionary, but a Dictionary and the PersonViewModel would have a IsChecked property and a Person property pointing to the model.
Otherwise, you have to go and find the checkbox in templates or get to the list box item from the checkbox and this gets complex pretty fast.
Josh Smith has an article on codeproject that should explain what you need. He is discussing a TreeView but the principle will port over to the CheckBox as well.
There is also a very interesting approach here using a DataTemplate and Binding the CheckBox.IsChecked property to the ListBoxItem.IsSelected property.
If you are new to MVVM, Jason Dolinger has an excellent video on the subject. It steps you through the process moving from using code behind files to a full MVVM pattern including Dependency Injection and Testing.
lets begin with the scenario:
I have an ItemsControl inside a UserControl. In this ItemsControl I have a dynamicly created DataTemplate which is created and added in codebehind. As there doesn't seem to be a nice way to create a DataTemplate in codebehind I had to programmatically generate the xaml code for my DataTemplate into a string and then create a DataTemplate object out of it through XamlReader:
StringBuilder stringBuilder = new StringBuilder();
XmlWriter xmlWriter = XmlWriter.Create(stringBuilder);
... // use xmlWrite to generate desired xaml
// substring is use to cut out the xml declaration
DataTemplate template = (DataTemplate)XamlReader.Load(stringBuilder.ToString().Substring(39));
myItemsControl.ItemTemplate = template;
The generated XAML code looks like this and is actually used (the items get rendered as expected):
<DataTemplate xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Grid HorizontalAlignment="Stretch" Margin="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding b0}" Grid.Column="0" />
<TextBox Text="{Binding b1, Converter={StaticResource customConverter}}" HorizontalAlignment="Stretch" Grid.Column="1" LostFocus="TxtAttribute_LostFocus" />
<TextBox Text="{Binding b2, Converter={StaticResource customConverter}}" HorizontalAlignment="Stretch" Grid.Column="2" LostFocus="TxtAttribute_LostFocus" />
<TextBox Text="{Binding b3, Converter={StaticResource customConverter}}" HorizontalAlignment="Stretch" Grid.Column="3" LostFocus="TxtAttribute_LostFocus" IsReadOnly="True" />
</Grid>
In case you wonder: the xmlns attribute is needed by the XamlReader to render the control, else you'll get an exception when reaching the code.
My problem:
now while the items look like expected and data is correctly bound neither my customConverter that should reformat the bound data, nor the LostFocus event are correctly applied. I don't get any error messages or warnings, converter and event just don't get called. Anyone an idea why and how I can get this to work?
Update:
I reached a point where I have to solve this problem or to try a different approach.
In my last tests I tried to add the Converter directly in the DataTemplate but I had no luck. The generated code now looks like this:
<DataTemplate xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Conv="clr-namespace:my.Namespace" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Grid HorizontalAlignment="Stretch" Margin="0,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="80" />
<ColumnDefinition Width="80" />
</Grid.ColumnDefinitions>
<Grid.Resources>
<Conv:DecimalConverter x:Name="cnvDecimalConverter" />
</Grid.Resources>
<TextBlock Text="{Binding b0}" Grid.Column="0" />
<TextBox Text="{Binding b1, Converter={StaticResource cnvItemsDecimalConverter}}" HorizontalAlignment="Stretch" Grid.Column="1" LostFocus="TxtAttribute_LostFocus" />
<TextBox Text="{Binding b2, Converter={StaticResource cnvItemsDecimalConverter}}" HorizontalAlignment="Stretch" Grid.Column="2" LostFocus="TxtAttribute_LostFocus" />
<TextBox Text="{Binding b3, Converter={StaticResource cnvItemsDecimalConverter}}" HorizontalAlignment="Stretch" Grid.Column="3" LostFocus="TxtAttribute_LostFocus" IsReadOnly="True" />
</Grid>
</DataTemplate>
Any ideas?
Update 2:
As I just found out XamlReader.Load() just is not able to hook up events. See this Thread in the Silverlight Forums
The Converters should work, I guess I still have some kind of namespace problem I don't see. I'm kind of out of options with my "simple" ItemsControl approach so I think it's time to look for another method to reach my needs.
Just to clear up the situation: It is not possible to generate dynamic DataTemplates with events through generating an xaml string and extract the control from this. The only option to parse xaml code with events is through Application.LoadComponent which needs a URI to work.
I ended up using nested ItemControls to create my "dynamic" behaviour.