WPF binding root element of xaml file does not work: - wpf

I was trying to bind a dataContext to a grid (xaml below)
<Grid .... DataContext="{Binding Path=NewFormViewModel}" > ...</Grid>
This binding did not work and I realized the getter for NewFormViewModel was never being called.
At this point in time, The grid was the root element of the xaml file.
I then placed a canvas inside the Grid and did binding on the canvas like :
<Grid ....>
<Canvas DataContext="{Binding Path=NewFormViewModel}">
....
</Canvas>
</Grid>
The data binding worked.
Next I tried to change the grid to a canvas and do databinding agiain like this:
<Canvas.... DataContext="{Binding Path=NewFormViewModel}" > ...</Canvas>
The binding stopped working again.
In the end I settled for a Grid nested inside a canvas:
<Canvas....>
<Grid DataContext="{Binding Path=NewFormViewModel}">
....
</Grid>
</Canvas>
The question is why did the binding on the root element of the xaml not work?
or Should I have not used a Canvas/Grid as a root element at all and used something like Page/UserControl?
EDIT
My logical tree looks somthing like this:
Window <- Data binding to object o
|
*
Frame <-Data binding to obect o inherited
|
*
Canvas/Grid <- Data binding to o.NewFormViewModel failed
|
*
Canvas/Grid <- Data binding to o.NewFormViewModel Succeeds
EDIT2:
broken xaml:
<Canvas x:Class="WPFEditors.NewForm"
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" Height="398" Width="377"
DataContext="{Binding Path=NewFormViewModel}"
>
<Grid >
<Label DataContext="Form Type" Height="31" HorizontalAlignment="Left" Margin="12,22,0,0" VerticalAlignment="Top" Width="92" />
<ComboBox Margin="148,22,6,347" ItemsSource="{Binding Path=FormTypes}" />
<Label Content="Description" Height="31" HorizontalAlignment="Left" Margin="12,58,0,0" VerticalAlignment="Top" Width="92" />
<Label Content="{Binding Path=Heading}" ToolTip="This is pulled from the Enum defined for FormTypes" Margin="148,59,6,309" />
<Label Content="Version" Height="31" HorizontalAlignment="Left" Margin="12,95,0,0" VerticalAlignment="Top" Width="92" />
<TextBox Text="Bind this later" Margin="148,97,6,270" Height="31" />
<Label Content="Approval Level" Height="31" HorizontalAlignment="Left" Margin="12,132,0,0" VerticalAlignment="Top" Width="92" />
<TextBox Height="31" Margin="148,134,6,233" Text="Bind this later" />
<Label Content="Number of Approvals" Height="31" HorizontalAlignment="Left" Margin="12,171,0,0" VerticalAlignment="Top" Width="130" />
<TextBox Height="31" Margin="148,173,6,194" Text="Bind this later" />
<Label Content="Heading" Height="31" HorizontalAlignment="Left" Margin="12,210,0,0" VerticalAlignment="Top" Width="130" />
<TextBox Height="31" Margin="148,212,6,155" Text="Bind this later" />
<Label Content="Static Data Id" Height="31" HorizontalAlignment="Left" Margin="12,247,0,0" VerticalAlignment="Top" Width="130" />
<TextBox Height="31" Margin="148,249,6,118" Text="Bind this later" />
<Label Content="{Binding Path=Errors}" Background="{Binding Path=Color}" Margin="12,325,6,6" BorderThickness="1" BorderBrush="#FF000019" />
<Button Content="Create" Margin="83,294,202,78" />
<Button Content="Create" Margin="181,294,104,78" />
<Button Content="Create" Margin="279,294,6,78" />
</Grid>
</Canvas>
In the above xaml the following line binds:
<Label Content="{Binding Path=Heading}"
even though Heading is inside a property of the viewmodel that this document inherits.
At this point I would have expected the line:
DataContext="{Binding Path=NewFormViewModel}"
to have changed the datacontext to NewFormViewModel which has no heading. All the remaining bindings fail.
If I change the beginning to :
<Canvas x:Class="NewForm"
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" Height="398" Width="377"
>
<Grid DataContext="{Binding Path=NewFormViewModel}" >
The binding to Heading fails and the rest of the bindings start to work. This is the behaviour that I was expecting initially.
This xaml is nested in this file :
<Window x:Class="WPFEditors.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="{Binding Path=Heading}" Height="743" Width="1177">
<StackPanel >
<Menu IsMainMenu="True" >
<MenuItem Header="New">
<MenuItem Header="New Form" Command="{Binding Path=MenuCommand}" />
</MenuItem>
<MenuItem Header="Edit Form" ItemsSource="{Binding Path=FormsAvailable}" />
<MenuItem Header="Edit Rules" />
</Menu>
<Frame NavigationUIVisibility="Hidden" Source="{Binding Path=CurrentPage}"
LoadCompleted="Frame_LoadCompleted"
DataContextChanged="Frame_DataContextChanged"
Name="frame">
</Frame>
</StackPanel>
</Window>
and the event handlers to copy the data context to the child are :
private void Frame_LoadCompleted(object sender, NavigationEventArgs e)
{
UpdateFrameDataContext();
}
private void Frame_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
UpdateFrameDataContext();
}
private void UpdateFrameDataContext()
{
var content = frame.Content as FrameworkElement;
if (content == null)
return;
content.DataContext = frame.DataContext;
}

The Binding in the root element should work IF there is something up in the control hierarchy (like a Window or UserControl) that holds (in its DataContext) the object owning the property you're querying (NewFormViewModel).

Related

WPF - DataBinding (Command is not being executed)

I am writing a WPF application where I wish to implement the MVM pattern.
My code is currently set up like this:
This is my view model class where I am exposing the command I wish to be executed as a property
```
public HomeScreenViewModel(NavigationService mw)
{
this.mw = mw;
}
private RelayCommand _addStudent;
public ICommand AddStudent
{
get
{
if (_addStudent == null)
{
_addStudent = new RelayCommand(param => this.OpenNewStudents(), param => true);
}
return _addStudent;
}
}
This is my xaml containing the layout of the corresponding view
<Page x:Class="test.HomeScreen"
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"
xmlns:local="clr-namespace:test"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="1300"
Title="HomeScreen">
<WrapPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<WrapPanel HorizontalAlignment="Center" Height="300" Margin="0,0,0,0" VerticalAlignment="Center" Width="1300">
<Button Content="AddStudent" Command="{Binding AddCommand}" Style="{StaticResource ButtonStyle}" />
<Button Content="AddStudent" Style="{StaticResource ButtonStyle}" />
<Button Content="AddStudent" Style="{StaticResource ButtonStyle}" />
<Button Content="AddStudent" Style="{StaticResource ButtonStyle}" />
<Button Content="AddStudent" Style="{StaticResource ButtonStyle}" />
<Button Content="AddStudent" Style="{StaticResource ButtonStyle}" />
<Button Content="AddStudent" Style="{StaticResource ButtonStyle}" />
<Button Content="AddStudent" Style="{StaticResource ButtonStyle}" />
<TextBox Text="{Binding Path=HI}" Width="200" Height="200"/>
</WrapPanel>
<Canvas Margin="350, 100,0, 0" Height="300" Width="1350" VerticalAlignment="Center" HorizontalAlignment="Center">
<Image Source="Logo.PNG"/>
</Canvas>
</WrapPanel>
I have set the command of the top button using the binding syntax in the WrapPanel.
This HomeScreen page is linked from a naviagation window and is the source of the navigation window (xaml file):
<NavigationWindow x:Class="test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:test"
mc:Ignorable="d"
Title="School Management App" Height="350" Width="1250"
Source="HomeScreen.xaml">
</NavigationWindow>
I have my HomeScreen view constructor set like this:
public partial class HomeScreen : Page
{
public HomeScreen()
{
InitializeComponent();
this.DataContext = new HomeScreenViewModel(this.NavigationService);
}
}
My infrastructure is set up as I have described above and my implementation of relay command is taken from this site https://msdn.microsoft.com/en-us/magazine/dd419663.aspx.
When I run my application and I press the corresponding button, nothing happens. I was wondering if someone could help me figure out what is wrong and why the command binding isn't working. It would be much appreciated, thanks :)
as a side note, could someone give me some tips on what the best way to debug wpf applications and figure out what bindings exist at run time.
Thanks :)
your command name is AddStudent, but you use AddCommand in xaml. Just correct the name:
Command="{Binding AddStudent}"

How to bind string of class to resource dictionary

I have many tooltips saved in file hints.xaml . Like this :
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp7.Theme"
x:Class="WpfApp7.Theme.Hints">
<ToolTip x:Key="myToolTip" >
<StackPanel >
<Label Content="Nadpis" />
<TextBlock Text="................"/>
</StackPanel>
</ToolTip>
</ResourceDictionary>
How i can bind string of class Enviroment.CurrentDictionary on my Text="................" ?
I tried to create code behind for Hints.xaml and bind by DataContext but didnt work .
Try this:
<StackPanel xmlns:system="clr-namespace:System;assembly=mscorlib">
<Label Content="Nadpis" />
<TextBlock Text="{x:Static system:Environment.CurrentDirectory}"/>
</StackPanel>

XAML Binding to custom control

I have built a custom control and want to know how to properly bind in XAML to a property of an item in an Observable collection in the custom control. The custom control property looks like this.
Public Property MyPoints As ObservableDependencyObjectCollection(Of MyPoint)
Get
Return CType(GetValue(MyPointsProperty), ObservableDependencyObjectCollection(Of MyPoint))
End Get
Set(value As ObservableDependencyObjectCollection(Of MyPoint))
SetValue(MyPointsProperty, value)
End Set
End Property
MyPoint contains two properties X and Y
Full XAML
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525" xmlns:my="clr-namespace:WpfDependencyPropertyVB">
<Grid>
<my:CustomControl1 HorizontalAlignment="Left" Margin="322,212,0,0" x:Name="CustomControl11" VerticalAlignment="Top" Width="145" Height="67">
<my:CustomControl1.MyPoints>
<my:MyPoint X="100" Y="{Binding Value, ElementName=Slider1}" />
</my:CustomControl1.MyPoints>
</my:CustomControl1>
<Slider Height="26" HorizontalAlignment="Left" Margin="23,174,0,0" Name="Slider1" VerticalAlignment="Top" Width="273" />
<Label Content="{Binding Value, ElementName=Slider1}" Height="41" HorizontalAlignment="Left" Margin="329,145,0,0" Name="Label1" VerticalAlignment="Top" Width="135" />
</Grid>
enter code here
In My XAML the set up looks like this:
<my:CustomControl1 x:Name="CustomControl11" >
<my:CustomControl1.MyPoints>
<my:MyPoint X="100" Y="{Binding Value, ElementName=Slider1}" />
</my:CustomControl1.MyPoints>
</my:CustomControl1>
If I set a break point in my control I can see that X=100 but i don't see that Y is updated when the Slider Value changes.
Any help would be greatly appreciated.
My solution would be to add a dependency property ThePoint to CustomControl1 and then bind the slider value to ThePoint.Y. I'm assuming that MyPoint derives from DependencyObject and that X and Y are dependency properties.
<my:CustomControl1 x:Name="theControl" />
<Slider Grid.Row="1" Value="{Binding ElementName=theControl,
Path=ThePoint.Y, Mode=TwoWay}"/>
You could then add PropertyChangedCallback handlers as required to add items to the collection (if that's what's required).
Alternatively you can bind to an item in the collection:
<local:MyCustomControl x:Name="theControl" />
<Slider Grid.Row="1" Value="{Binding ElementName=theControl,
Path=MyPoints[0].Y, Mode=TwoWay}" />

TreeView Within Data Template

I have an object that looks like:
List =
Parent+Child,
Parent+Child
Parent+Child
Parent+List<Child>
Parent+Child
Parent+Child have been flattened out to aid binding whilst I get this working.
I am trying to write them to a templated list box, using a different template for if there is 1 child, and another template if there is more than 1 child. I have got this working, but I want to bind the Multiple Children to a TreeView, so you can expand out the Parent and see all the Children, but I am struggling to get this to bind.
Here is my code (in logical order):
<ListBox x:Name="lsbGrandParent" ItemTemplateSelector="{StaticResource dataTemplateSelector}" />
<localutilities:dataTemplateSelector
OneChildTemplate="{StaticResource dtOneChildSelected}"
MultipleChildrenTemplate="{StaticResource dtMultipleChildrenSelected}"
x:Key="dataTemplateSelector"/>
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
int childCount = Convert.ToInt32(item).Count;
if (childCount <= 1)
return OneChildTemplate;
else
return MultipleChildrenTemplate;
}
<DataTemplate x:Name="dtMultipleChildrenSelected" x:Key="dtMultipleChildrenSelected">
<Grid HorizontalAlignment="Left" VerticalAlignment="Center">
<StackPanel Orientation="Vertical" >
<TreeView HorizontalAlignment="Left" ItemTemplate="{StaticResource menuMultipleChildren}" Width="220" Height="100" Margin="0,0,0,0" MaxHeight="220" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" />
</StackPanel>
</Grid>
</DataTemplate>
<HierarchicalDataTemplate x:Key="menuMultipleChildren" ItemsSource="{Binding Path=MultipleChildren}" ItemTemplate="{StaticResource menuChildren}">
<StackPanel Orientation="Horizontal">
<Label Content="{Binding Path=ParentId}" Width="20" Height="Auto" />
<Label Content="{Binding Path=ParentName}" Width="140" Height="Auto" />
</HierarchicalDataTemplate>
<HierarchicalDataTemplate x:Key="menuChildren">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Bottom" >
<Label Content="{Binding Path=ChildName}" Width="140" Height="Auto" />
</StackPanel>
</HierarchicalDataTemplate>
But when it runs there is just a space where the treeview should be, and I have put a break on when the ParentName binds and it is not binding. If I put the template code directly in the TreeViewItem it binds but does not display the value.
Am I doing something obviously wrong, or is there a better way to achieve what Im trying to achieve?

Binding a wpf listbox to a combobox

I have created a very basic wpf application that I want to use to record time entries against different projects.
I havent used mvvm for this as I think its an overkill.
I have a form that contains a combobox and a listbox. I have created a basic entity model like this
What I am trying to do is bind the combobox to Project and whenever I select an item from the combobox it updates the listview with the available tasks associated with that project.
This is my xaml so far. I dont have any code behind as I have simply clicked on that Data menu and then datasources and dragged and dropped the items over. The application loads ok and the combobox is been populated however nothing is displaying in the listbox.
Can anyone tell me what I have missed?
<Window.Resources>
<CollectionViewSource x:Key="tasksViewSource" d:DesignSource="{d:DesignInstance l:Task, CreateList=True}" />
<CollectionViewSource x:Key="projectsViewSource" d:DesignSource="{d:DesignInstance l:Project, CreateList=True}" />
</Window.Resources>
<Grid DataContext="{StaticResource tasksViewSource}">
<l:NotificationAreaIcon
Text="Time Management"
Icon="Resources\NotificationAreaIcon.ico"
MouseDoubleClick="OnNotificationAreaIconDoubleClick">
<l:NotificationAreaIcon.MenuItems>
<forms:MenuItem Text="Open" Click="OnMenuItemOpenClick" DefaultItem="True" />
<forms:MenuItem Text="-" />
<forms:MenuItem Text="Exit" Click="OnMenuItemExitClick" />
</l:NotificationAreaIcon.MenuItems>
</l:NotificationAreaIcon>
<Button Content="Insert" Height="23" HorizontalAlignment="Left" Margin="150,223,0,0" Name="btnInsert" VerticalAlignment="Top" Width="46" Click="btnInsert_Click" />
<ComboBox Height="23" HorizontalAlignment="Left" Margin="70,16,0,0" Name="comProjects" VerticalAlignment="Top" Width="177" DisplayMemberPath="Project1" ItemsSource="{Binding Source={StaticResource projectsViewSource}}" SelectedValuePath="ProjectID" />
<Label Content="Projects" Height="28" HorizontalAlignment="Left" Margin="12,12,0,0" Name="label1" VerticalAlignment="Top" IsEnabled="False" />
<Label Content="Tasks" Height="28" HorizontalAlignment="Left" Margin="16,61,0,0" Name="label2" VerticalAlignment="Top" />
<ListBox Height="112" HorizontalAlignment="Left" Margin="16,87,0,0" Name="lstTasks" VerticalAlignment="Top" Width="231" DisplayMemberPath="Task1" ItemsSource="{Binding Path=ProjectID, Source=comProjects}" SelectedValuePath="TaskID" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="101,224,0,0" Name="txtMinutes" VerticalAlignment="Top" Width="42" />
<Label Content="Mins to Insert" Height="28" HorizontalAlignment="Left" Margin="12,224,0,0" Name="label3" VerticalAlignment="Top" />
<Button Content="None" Height="23" HorizontalAlignment="Left" Margin="203,223,0,0" Name="btnNone" VerticalAlignment="Top" Width="44" />
</Grid>
You set the DataContext in the Grid, so just change your ItemsSource as indicated below.
<Grid DataContext="{StaticResource tasksViewSource}">
<ListBox Height="112"
HorizontalAlignment="Left"
Margin="16,87,0,0"
Name="lstTasks"
VerticalAlignment="Top"
Width="231"
DisplayMemberPath="Task1"
ItemsSource="{Binding}"
SelectedValuePath="TaskID" />
</Grid>
You'll also want to filter the task list, to do that just change the generated code.
Here is an example I hacked together.
You can toggle the value using the SelectionChanged event from your ComboBox.
private System.Data.Objects.ObjectQuery<Models.Task> GetTasksQuery(Models.StackoverflowEntities stackoverflowEntities)
{
// get the selected item
int id = (int)cboProjects.SelectedValue;
System.Data.Objects.ObjectQuery<CollectionViewSourceEF.Models.Task> tasksQuery = stackoverflowEntities.Tasks.Where(task => task.ProjectID == id) as ObjectQuery<Task>;
return tasksQuery;
}

Resources