I have a WPF project which uses, as best I can, the MVVM pattern. I'm using Visual Studio 2010, and the language behind is C#.
So I have a treeview. The treeview looks like this:
<TreeView Grid.Column="0" Width="Auto" Background="Aquamarine" ItemsSource="{Binding Scenes}" SelectedItemChanged="TreeView_SelectedItemChanged">
<TreeView.DataContext>
<viewModels:ScenesViewModel />
</TreeView.DataContext>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Characters}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding SceneName}"></TextBlock>
<Image Source="{StaticResource ImgWorld}" Margin="0,0,5,0" Width="32" Height="32"/>
</StackPanel>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}"></TextBlock>
<Border BorderThickness="1" Background="RoyalBlue">
<Image Source="{Binding ImgIcon}" Margin="2"/>
</Border>
</StackPanel>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
The point of contention is the Image, the source of which is bound to something called 'ImgIcon'. This is a property of a view model called 'CharacterViewModel'. For some reason I am getting nothing displayed where the image should be.
The most obvious issue might be regarding the data context or whether the MVVM side of things is working. I am fairly sure it is, as there is a TextBlock next to the Image which is also bound to a property called 'FirstName'. This binding works fine.
The two properties are, of course, in the view model, and can be seen here:
public String FirstName
{
get { return Character.FirstName; }
set
{
if (Character.FirstName != value)
{
Character.FirstName = value;
RaisePropertyChanged("FirstName");
}
}
}
private BitmapImage _imgIcon = null;
public BitmapImage ImgIcon
{
get { return _imgIcon; }
set
{
_imgIcon = value;
RaisePropertyChanged("ImgIcon");
}
}
They are slightly different in that the latter does not wrap around a property in the Character class.
So the next possible fault would be the loading of the image. This is accomplished by this method (which at the moment just has a test path for a known working file):
public bool LoadIcon()
{
if (ImgIcon == null)
{
Uri outUri;
if (Uri.TryCreate(#"D:\Pictures\Icons\HCAria01.png", UriKind.Absolute, out outUri) )
{
}
ImgIcon = new BitmapImage(outUri);
return true;
}
else
return false;
}
I put a break point in at 'ImgIcon = new BitmapImage(outUri)' and you can see the relevant information here: picture. It seems from this picture that the image has loaded successfully.
I'm sure I'm missing something obvious, but I'm damned if I can say what it is. From my limited experience with WPF and XAML, it seems to be working okay, except for the lack of an image.
Well, if you can offer any help that would be much appreciated.
Edit: a couple of clarifications. The DataContext for the Treeview is called ScenesViewModel. This holds a list of SceneViewModel, and that list is called 'Scenes'. Each SceneViewModel containst a list of CharacterViewModel, and that is called 'Characters'. These two images show the relevant part of those classes, so hopefully the hierarchy can be understood. For each CharacterViewModel, there should be an ImgIcon.
Make sure that you bind to the view model (ScenesViewModel) property if the ImgIcon property is defined in this class:
<Image Source="{Binding DataContext.ImgIcon, RelativeSource={RelativeSource AncestorType=TreeView}}" Margin="2"/>
The DataContext of the StackPanel in the ItemTemplate is an item in the ItemsSource and not the view model.
Edit: You should define one template for SceneViewModel objects and another one for CharacterViewModel objects. You could put the implicit templates in the section of the TreeView:
<TreeView Grid.Column="0" Width="Auto" Background="Aquamarine" ItemsSource="{Binding Scenes}"
SelectedItemChanged="TreeView_SelectedItemChanged">
<TreeView.DataContext>
<viewModels:ScenesViewModel />
</TreeView.DataContext>
<TreeView.Resources>
<HierarchicalDataTemplate DataType="{x:Type local:SceneViewModel}" ItemsSource="{Binding Characters}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding SceneName}"></TextBlock>
<Image Source="{StaticResource ImgWorld}" Margin="0,0,5,0" Width="32" Height="32"/>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:CharacterViewModel}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding FirstName}"></TextBlock>
<Border BorderThickness="1" Background="RoyalBlue">
<Image Source="{Binding ImgIcon}" Margin="2"/>
</Border>
</StackPanel>
</DataTemplate>
</TreeView.Resources>
</TreeView>
This answer is for those looking to see what happened - maybe you have the same issue.
My problem was actually elsewhere in my code. When I loaded a Scene (data model) from my database to populate the SceneViewModel's data, I created one instance which I used to load the image, and then added a new instance to the list. So there were two instances, one which was created, used to load the images and then not used, and another which was used (but which didn't have the images loaded). Basically it was a simple mistake and not related to the code seen here - that all worked fine (since now that I corrected the error, things work as they should, images and all).
Thanks to all those who took the time to answer.
Related
I have a treeview. In the treeview I have Student - Homework
The Homework is a Class with Hineritance, so under Homerwork I have -> ScienceH, PhysicsH, MathH
How can I create a XAML that is bound to the Homework list but changes depending on the "child" specifics?
I'm blocked as I do not understand how to do the binding
class Student
{
public string Name;
List<Homework> homeworks;
}
class Homework
{
public DateTime Date;
public int Vote;
}
class ScienceH : Homework
{
public string Topic;
}
class PhysicsH : Homework
{
public int Points;
}
I expect to have an item that shows the additional fields depending on which child they have
You can define a TreeView in UWP XAML like this, as detailed here:
<TreeView ItemsSource="{x:Bind ListOfStudents}">
<TreeView.ItemTemplate>
<DataTemplate x:DataType="model:Student">
<TreeViewItem ItemsSource="{x:Bind homeworks}"
Content="{x:Bind Name}"/>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
You would then need to define a DataTemplate for each of class you expect to display. Add this in your TreeView XAML:
<TreeView.Resources>
<DataTemplate x:DataType="model:ScienceH">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Date}" />
<TextBlock Text="{Binding Vote}" />
</StackPanel>
</DataTemplate>
<DataTemplate x:DataType="model:PhysicsH ">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Date}" />
<TextBlock Text="{Binding Vote}" />
<TextBlock Text="{Binding Points}" />
</StackPanel>
</DataTemplate>
</TreeView.Resources>
You can also read more on the DataTemplate class here.
Note that, as suggested in the comments, you need to implement change notifications in your classes to ensure correct binding. This is all detailed here.
Alternatively, you seem to have a TreeView with a tree structure of known, fixed depth: depth 0 would be the list of Student and depth 1 would be the list of Homework, so you could also do without TreeView and simply nest two ItemsControl within one another.
<Grid.Resources>
<DataTemplate x:Key="trackTemplateY">
<TextBlock x:Name="txbValueY" Text="{Binding ValueX}" Margin="5" FontSize="11" FontWeight="Medium"/>
</DataTemplate>
</Grid.Resources>
<TextBlock Text="{Binding ElementName=txbValueY,Mode=OneWay,Path=Text}"
Background="Orange" Foreground="White"/>
I try this above code but i cant to bind the text, how can i bind inside resources textblock text to outside the resources, Thanks
I am guessing that you are trying to show Text present in TextBlock resource in your second non-resource TextBlock.
You don't need DataTemplate. As you will progress ahead in WPF journey, you will come to know about those.
Below code will show "Resource Text" in your second TextBlock.
<Grid.Resources>
<TextBlock x:Key="TbRes1" Text="Resource Text" x:Name="txbValueY" Margin="5" FontSize="11" FontWeight="Medium"/>
</Grid.Resources>
<TextBlock Text="{Binding Source={StaticResource TbRes1},Mode=OneWay,Path=Text}"
Background="Orange" Foreground="White"/>
All sorts of problems here:
You're specifying Mode.TwoWay in your TextBlock Text binding, it should be Mode.OneWay.
You're binding to the Label's Text property. Label doesn't have a Text property, only Content. And it's not a dependency property so you can't bind to it. (That said, a fluke of the internal mechanics does cause it to appear to work under certain conditions).
A template is exactly that: a template. You can't bind to something that doesn't exist, so the binding is meaningless.
Maybe you could clarify exactly what it is you're trying to do so we can suggest an alternative way of achieving it? Specifically, show us exactly how you're instantiating that DataTemplate.
UPDATE:
You need the first textbox to be created in order for the second one to bind to it, simply declaring it inside a DataTemplate doesn't cause that to happen by itself, so the direct binding will fail. Binding UI elements together like this should generally be avoided though, why can't you simply give the second textbox the same binding as the first?
<Grid>
<Grid.Resources>
<DataTemplate x:Key="trackTemplateY">
<TextBlock x:Name="txbValueY" Text="{Binding ValueX}" Margin="5" FontSize="11" FontWeight="Medium"/>
</DataTemplate>
</Grid.Resources>
<TextBlock Text="{Binding ValueX}" Background="Orange" Foreground="White"/>
</Grid>
If for some reason this isn't possible then you can also create a binding proxy object (see this page for details):
<Grid>
<Grid.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding ValueX}" />
<DataTemplate x:Key="trackTemplateY">
<TextBlock x:Name="txbValueY" Text="{Binding ValueX}" Margin="5" FontSize="11" FontWeight="Medium"/>
</DataTemplate>
</Grid.Resources>
<TextBlock Text="{Binding Source={StaticResource ResourceKey=proxy}, Path=Data}" Background="Orange" Foreground="White"/>
</Grid>
Again, there are ways to bind to the data template declaration if you really want to, but to do that I'd have to see details of how your data template is being created at runtime.
My Windows 8 c#/xaml app has 3 big Grids with the Content inside. Each one fills exactly out the screen. This System works very well for my purpose, until the user snapps my app.
So is it possible to completly change the Grid definitions, or pull all the textboxes,buttons and listviews in a other "snapped" Grid? Last one is just an idea.
private void pageRoot_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (ApplicationView.Value == ApplicationViewState.Snapped)
{
????
}
else
{
Grid1.Width = Windows.UI.Xaml.Window.Current.Bounds.Width;
Grid2.Width = Windows.UI.Xaml.Window.Current.Bounds.Width;
Grid3.Width = Windows.UI.Xaml.Window.Current.Bounds.Width;
}
}
This is a scenario where MVVM becomes incredibly handy. By creating two separate views, one each for snapped, filled, and full screen, you can swap between them relatively easy.
Your other option is to use the new FlipView control. There's a great example of this in the Contoso Cookbook sample app that can be found in the Windows 8 Dev Camp in a Box.
http://bit.ly/win8RCdevcamp
Here's the example code from the Contoso Hands-On Lab:
<FlipView.ItemTemplate>
<DataTemplate>
<UserControl Loaded="StartLayoutUpdates" Unloaded="StopLayoutUpdates">
<ScrollViewer x:Name="scrollViewer" Style="{StaticResource VerticalScrollViewerStyle}" Grid.Row="1">
<!-- Vertical StackPanel for item-detail layout -->
<StackPanel Orientation="Vertical" Margin="20,0,20,0">
<StackPanel Orientation="Vertical">
<TextBlock FontSize="20" FontWeight="Light" Text="{Binding Title}" TextWrapping="Wrap"/>
<Image x:Name="image" Width="260" Margin="0,12,0,40" Stretch="Uniform" Source="{Binding Image}" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Orientation="Vertical">
<TextBlock FontSize="20" FontWeight="Light" Text="Ingredients" Margin="0,0,0,16"/>
<TextBlock FontSize="16" FontWeight="Light" TextWrapping="Wrap" Text="{Binding Ingredients, Converter={StaticResource ListConverter}}" />
</StackPanel>
</StackPanel>
</ScrollViewer>
</UserControl>
</DataTemplate>
</FlipView.ItemTemplate>
As you can see, for each FlipView, a different display state is referenced. I'd recommend checking out that hands-on lab for a more specific look at your situation, or this other sample that includes both HTML and XAML examples of the FlipView control:
http://code.msdn.microsoft.com/windowsapps/FlipView-control-sample-18e434b4
In your scenario I would navigate to a different page when I detected the change to snapped and load a page that has a snap optimized experience rather than trying to dynamically update the layout of a complex page.
I have a window with the following elements, and I'm trying to access the value contained in <TextBlock Name="armingValue" but in my .xaml.cs file it doesn't seem to be recognised.
What do I need to do to access the value?
<Window.Resources>
<DataTemplate DataType="{x:Type ArmingVM:ArmingItem}">
<CheckBox Margin="10,5" IsChecked="{Binding IsSet}" Content="{Binding Name}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type ArmingVM:ArmingBindingData}">
<DockPanel>
<ItemsControl ItemsSource="{Binding ArmingItems}" HorizontalAlignment="Left">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<TextBlock Text="Enum Value: " HorizontalAlignment="Right"/>
<TextBlock Name="armingValue" Text="{Binding Value}" HorizontalAlignment="Right"/>
</DockPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="193*" />
<ColumnDefinition Width="551*" />
</Grid.ColumnDefinitions>
<Button Content="Get Panel Options" Name="btnGetOptionsConfigruation" Margin="12,12,23,396" Click="btnGetOptionsConfigruation_Click"></Button>
<StackPanel Grid.Column="1" Height="325" HorizontalAlignment="Left" Margin="68,43,0,0" Name="stackPanel1" VerticalAlignment="Top" Width="438">
<ItemsControl Name="armingItemsControl" ItemsSource="{Binding}"/>
</StackPanel>
</Grid>
The backing variables generated by visual studio within the .xaml.cs file are only generated for certain circumstances. Any 'named' element within the body of a user control will have a generated backing variable. However, named elements within templates will not be generated. This is because Visual Studio has no way of knowing how your template will be used. For example, your template could be used by an ItemsControl to generate multiple template instances. What should be generated within .xaml.cs in that case?
You have two options:
Use binding, so that the state of your TextBlock.Text property is bound to a view model, so that you do not have to access the TextBlock element directly.
'walk' the visual tree to locate your TextBlock at runtime.
For (2), I would suggest using Linq-to-VisualTree, where you can find your TextBlock as follows:
TextBlock block = layoutRoot.Descendants<TextBlock>()
.Cast<TextBlock>().Where(tb => tb.Name="armingValue")
.Single();
You do not need to access TextBox value but its binded value.
So considering that you have in XAML
<TextBlock Name="armingValue" Text="{Binding Value}" HorizontalAlignment="Right"/>
You need to read a Value
Try always to avoid access UI elements directly in WPF, cause sometimes (not so rare cases) it becomes really tricky to find them if not imossible (I mean not guranteed way). Access a Data that stands behind them.
Maybe I did not get the point but why don't you create a binding to textbox and mark it as two way?
<TextBlock Text="Enum Value: " HorizontalAlignment="Right" Text="{Binding Value, Mode=TwoWay}"/>
I have a listBox and add data to it like so:
for (int x = 0; x < Orchestrator.Instance.getTopicCount(); x++)
{
listTopics.Items.Add(Orchestrator.Instance.getTopic(x));
}
But I need to be able to do things like have text wrapping and divider lines, so I would like to do it the XAML.
Microsoft shows this:
<TextBlock Width="248" Height="24"
Text="{Binding ElementName=lbColor, Path=SelectedItem.Content,
Mode=OneWay}"
x:Name="tbSelectedColor"
Background="{Binding ElementName=lbColor, Path=SelectedItem.Content,
Mode=OneWay}"/>
But I don't really understand it. Here is my XAML:
<ListBox Height="261" HorizontalAlignment="Left" Margin="352,38,0,0" Name="listContent" VerticalAlignment="Top" Width="391" Grid.Column="1" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" MaxWidth="391" ScrollViewer.HorizontalScrollBarVisibility="Disabled"/>
How am I able to achieve what I want? (Divider lines, text wrapping so I don't have to scroll horizontally, and data binding)
To specify the layout of each item, there are two different things you can change: the ItemContainerStyle, which provides the ControlTemplate used for each ListBoxItem, or the ItemTemplate, which provides the DataTemplate that is used to render each data item. The ItemTemplate is simpler to use if you're just getting started and haven't gotten the hang of ControlTemplates yet.
To get your text to wrap, there are two key things to do. Turn off the default horizontal scrolling of ListBox (which you got already), and set the TextWrapping property of your TextBlock to Wrap. To get to the TextBlock you need to define it in your ItemTemplate. Here's a simple example of the template declared inline, though you could also pull it out as a Resource. For the dividing line I used the simplest approach of a Border with only a bottom black line.
<ListBox x:Name="listContent" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderThickness="0,0,0,1" BorderBrush="Black">
<TextBlock Text="{Binding}" TextWrapping="Wrap"/>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
It seems that you will have to ascend the rather steep learning curve for how to use databinding in WPF first. Thereafter you should learn about DataTemplates and ItemTemplates to get the dividers and so forth.
Try this to get you started
A book I can heartily recommend is WPF in Action from Manning.
You need to define the ItemTemplate of your ListBox.
In your resources, add this:
<DataTemplate x:Key="myItemTemplate" TargetType="ListBoxItem">
<TextBlock Text="{Binding}"/>
</DataTemplate>
Supposing that your getTopic method returns a string, otherwise use {Binding MyTopicProperty} where MyTopicProperty is a property in your Topic class. Customize the TextBlock as you need.
Then use your ListBox like this:
<ListBox ItemTemplate="{StaticResource myItemTemplate"/>
here is an example how to bind listbox with RSS feed with DataTemplate:
<UserControl.Resources>
<XmlDataProvider x:Key ="DataRSS" XPath="//item" Source="http://rss.feedsportal.com/c/629/f/502199/index.rss">< /XmlDataProvider>
</UserControl.Resources>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<ListBox ItemsSource="{Binding Source={StaticResource DataRSS}}" Height="516" Margin="0,0,32,0" Background="{x:Null}" BorderBrush="#FF627DAE">
<ListBox.ItemTemplate >
<DataTemplate >
<Grid Width="400" Height="100" >
<Image Source="{Binding XPath=enclosure/#url}" Grid.Column="0" HorizontalAlignment="Left" VerticalAlignment="Top" />
<TextBlock TextWrapping="Wrap" Text="{Binding XPath=title}" FontWeight="Bold" Grid.Column="2"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</grid>