Problem with nested StackPanels and DataContext inheritance in Silverlight 4 - silverlight

I have a problem with nested StackPanels. If I define StackPanels like in the code below,
button binds to command (MVVM and command pattern) but doesn't react on button click (command function is not called). When I put stackPanel4 on second position (below stackPanel3) everything works ok. When I move stackPanel4 on the last position (I have more StackPanels then two inside parent StackPanel), button is not bound and command function is not called. What could be a reason for this behavior?
If I set DataContext for stackPanel4 it works ok.
<StackPanel DataContext="{StaticResource vmUserMasterData}" Grid.Column="1" Height="320" HorizontalAlignment="Left" Margin="4,6,4,0" Name="stackPanel1" VerticalAlignment="Top" Width="491">
<StackPanel Height="40" Name="stackPanel4" Orientation="Horizontal" Width="440">
<Button Content="Update User Data" Name="button1" Height="23" Width="440" Command="{Binding Path=UpdateDataCommand}"/>
</StackPanel>
<StackPanel Height="31" Name="stackPanel3" Orientation="Horizontal" Width="440">
<sdk:Label Content="Username" Height="28" Name="label2" Width="74" />
<TextBox Height="23" Name="textBox2" Width="365" Text="{Binding Username, Mode=OneWay}" />
</StackPanel>
</StackPanel>

There must be more to this problem. We have reproduced your setup and the command event fires every time, regardless or the order of the StackPanels (basically as you would expect it to work).
Can you provide any source from your ViewModel (specifically relating to how the command is created/used)? The description of the XAML, for the case that fails, is not clear. Can you provide an exact copy of the Xaml when it fails to fire? It might be a specific combination of panels you have on your machine. One wrong closure and it would fail (e.g. if it fell outside stackPanel1 by accident).
Our reproduction (which works regardless of panel order):
Xaml:
<UserControl.Resources>
<SilverlightApplication1:ViewModel x:Key="vmUserMasterData"/>
</UserControl.Resources>
<Grid x:Name="LayoutRoot">
<StackPanel DataContext="{StaticResource vmUserMasterData}" Grid.Column="1" Height="320" HorizontalAlignment="Left" Margin="4,6,4,0" Name="stackPanel1" VerticalAlignment="Top" Width="491">
<StackPanel Height="31" Name="stackPanel3" Orientation="Horizontal" Width="440">
<sdk:Label Content="Username" Height="28" Name="label2" Width="74" />
<TextBox Height="23" Name="textBox2" Width="365" Text="{Binding Username, Mode=OneWay}" />
</StackPanel>
<StackPanel Height="40" Name="stackPanel4" Orientation="Horizontal" Width="440">
<Button Content="Update User Data" Name="button1" Height="23" Width="440" Command="{Binding Path=UpdateDataCommand}"/>
</StackPanel>
</StackPanel>
</Grid>
ViewModel code:
using System.Windows.Input;
using Microsoft.Practices.Prism.Commands;
namespace SilverlightApplication1
{
public class ViewModel
{
public ICommand UpdateDataCommand { get; set; }
public string Username { get; set; }
public ViewModel()
{
Username = "Woof!";
UpdateDataCommand = new DelegateCommand<string>(
(x) =>
{
int dog = 1; // breakpoint here
});
}
}
}

Related

Why do slider events stop after reactivation?

I am working with a Windows Phone 8.1 Silverlight application that has instances of various controls on the page, buttons, check boxes, radio buttons, etc. and a slider.
Each control has an event handler added as follows, this example is specific to the slider, but other controls just replace the type of control and the actual event handled:
static void instrumentSlider(Slider slider)
{
Logger.Logger.LogMessage("instrumentSlider() - Called for: [" + slider + "]");
slider.ManipulationCompleted -= new EventHandler<ManipulationCompletedEventArgs>(slider_ManipulationCompleted);
slider.ManipulationCompleted += new EventHandler<ManipulationCompletedEventArgs>(slider_ManipulationCompleted);
}
static void slider_ManipulationCompleted(object sender, ManipulationCompletedEventArgse)
{
try
{
Logger.Logger.LogMessage("slider_ManipulationCompleted() - Called for: [" + sender + "]");
if (sender is Slider)
{
Slider slider = (Slider)sender;
// Do stuff...
}
}
catch (Exception ex)
{
Logger.Logger.LogException(ex);
}
}
I can download the app to the phone using visual Studio 2013 and everything works as expected, when I press buttons or move the slider I see the logging messages I expect to see.
At some point I accidentally pressed the camera button on the phone and my app was deactivated and the camera app activated. I switched back to my app and all the controls except the slider continue to produce events!
What am I missing here? I've tried several events for the slider, manipulation completed, lost focus, value changed, and they all stop being delivered after the app is reactivated, but events from all other controls are still being delivered.
Updated to include an XAML fragment defining the controls:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Slider Height="84" HorizontalAlignment="Left" Margin="37,243,0,0" Name="slider1" VerticalAlignment="Top" Width="383" />
<Button Content="Button" Height="72" HorizontalAlignment="Left" Margin="27,18,0,0" Name="button1" VerticalAlignment="Top" Width="160" Click="button1_Click" RenderTransformOrigin="0.531,-0.222" />
<CheckBox Content="CheckBox" Height="72" HorizontalAlignment="Left" Margin="241,17,0,0" Name="checkBox1" VerticalAlignment="Top" RenderTransformOrigin="0.509,0.181" />
<HyperlinkButton Content="HyperlinkButton" Height="30" HorizontalAlignment="Left" Margin="27,110,0,0" Name="hyperlinkButton1" VerticalAlignment="Top" Width="200" />
<ListBox Height="103" HorizontalAlignment="Left" Margin="37,342,0,0" Name="listBox1" VerticalAlignment="Top" Width="383" RenderTransformOrigin="0.499,1.408">
<ListBoxItem Content="ListItem1" />
<ListBoxItem Content="ListItem2" />
<ListBoxItem Content="ListItem3" />
</ListBox>
<RadioButton Content="RadioButton" Height="72" HorizontalAlignment="Left" Margin="241,73,0,0" Name="radioButton1" VerticalAlignment="Top" IsChecked="True" />
<RadioButton Content="RadioButton" Height="72" HorizontalAlignment="Left" Margin="241,126,0,0" Name="radioButton2" VerticalAlignment="Top" RenderTransformOrigin="0.528,0.056" />
<RadioButton Content="RadioButton" Height="72" HorizontalAlignment="Left" Margin="241,171,0,0" x:Name="radioButton3" VerticalAlignment="Top" RenderTransformOrigin="0.503,1.837" />
<TextBox Height="72" HorizontalAlignment="Left" Margin="37,445,0,0" Name="textBox1" Text="TextBox" VerticalAlignment="Top" Width="383" />
</Grid>

MVVM : Binding Commands with Collection to Listbox in WPF

i have a list box in which there are different controls like button , text box etc in its item template, i have a collection which i bind with listbox, it works fine , but now i want to move my code to MVVM , and i write some commands in my View Model for clicks events of buttons , how can i bind my collection + my commands to list box ??? because commands are not in the collection, this is the Data Template for my list Box
<DataTemplate x:Key="listItemTemplate">
<Grid ShowGridLines="False">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Name="commentsPanel" LastChildFill="False" MinWidth="350">
<TextBlock Name="txtUserName" IsEnabled="False" Text="{Binding UserName}"
Width="Auto" DockPanel.Dock="Left" Foreground="GhostWhite" Margin="0,6,0,0"></TextBlock>
<TextBlock Name="txtDate" IsEnabled="False" Text="{Binding CreateDt}"
Width="Auto" DockPanel.Dock="Left" Foreground="Green" Margin="4,6,0,0"></TextBlock>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" Width="{Binding EditPanelWidth}" x:Name="EditDeletePanel" Visibility="{Binding ButtonVisibilityText }">
<Button Name="btnEdit" Content="Edit" Width="Auto" DockPanel.Dock="Right" Height="20"
Click="btnEdit_Click_1" Margin="4,4,0,4" Foreground="GhostWhite" VerticalContentAlignment="Top" Visibility="{Binding ButtonVisibilityText}"></Button>
<Button Name="btnDelete" Content="Delete" Width="Auto" Height="20" VerticalContentAlignment="Top" DockPanel.Dock="Right" Visibility="{Binding ButtonVisibilityText}"
Click="btnDelete_Click_1" Margin="4"></Button>
</StackPanel>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" x:Name="SaveCancelPanel" Visibility="{Binding CancelSaveEnableText}">
<Button Name="btnSave" Content="Save" Width="Auto" Height="20" DockPanel.Dock="Right"
Click="btnSave_Click_1" Margin="4"></Button>
<Button Name="btnCancel" Content="Cancel" Height="20" Width="Auto" DockPanel.Dock="Right"
Click="btnCancel_Click_1" Margin="4"></Button>
</StackPanel>
</DockPanel>
<dxe:TextEdit ShowBorder="False" Grid.Row="1" Name="txtComment" Width="Auto" Foreground="Red"
TextWrapping="WrapWithOverflow" EditValue="{Binding Note}" IsEnabled="{Binding IsCommentTextEnable}">
</dxe:TextEdit>
<dxe:TextEdit Text=".............." Grid.Row="2" ShowBorder="False" IsEnabled="False">
</dxe:TextEdit>
</Grid>
</DataTemplate>
and here is the collection + my commands which i want to bind to buttons ,
public ICommand CancelCommand
{
get { return _cancelCommand ?? (_cancelCommand = new CommandHandler(Cancel)); }
set { _cancelCommand = value; }
}
public TList<ProgramNote> NotesCollection
{
get { return _notes; }
set
{
_notes = value;
RaisePropertyChanged("NotesCollection");
}
}
I know i can use this code to bind my commands with button
<Button Command={Binding CancelCommand}
but this command is not present in the collection , i am new in MVVM , kindly help , may be i am missing some little thing to bind my commands , but i am confused that how to add commands in my collection , so that i can get them in my view
You can bind the commands to your data template buttons etc by finding the appropriate viewmodel
example
<DataTemplate x:Key="listItemTemplate">
<Button Command="{Binding DataContext.CancelCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=LixtBox}}"
CommandParameter="{Binding}">
</DataTemplate>
in example we'll find the datacontext of LixtBox which I assume to be your viewmodel then will bind to the command and pass the current object as the command parameter to perform actions on.
you'll then receive the item as parameter in the implementation of your command
public void Execute(object parameter)
{
ProgramNote note = parameter as ProgramNote;
//your logic here, eg cancelling download etc.
}
Thanx to all of you specially thanx to #Sheridan and #PushPraj, I am able to do it now , here is the code of data template in which i have a button
<Button Name="btnCancel" Content="Cancel" Height="20" Width="Auto" DockPanel.Dock="Right"
Command="{Binding DataContext.CancelCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dxe:ListBoxEdit}}"
CommandParameter="{Binding}" Margin="4"></Button>
and this is the code of ListBox
<dxe:ListBoxEdit Name="listComments" Grid.Row="1" ItemTemplate="{StaticResource listItemTemplate}"
ItemsSource="{Binding NotesCollection}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Visible" >
</dxe:ListBoxEdit>
and lastly this is my back end code
listComments.DataContext = viewModel;
It's always difficult to answer new user's questions because they always leave out important information from their questions. However, judging by your question text, it sounds to me like you have set your collection property as the DataContext of your Window. If you want to data bind to your commands instead, then you'll need to change the DataContext to the object that contains both the collection and the commands... an instance of your view model:
DataContext = new YouViewModel();
Now that the DataContext is set to an instance of your view model, you can data bind to its properties as you showed us:
<Button Command="{Binding CancelCommand}" />
...
<ListBox ItemsSource="{Binding NotesCollection}" />
Ahhh, sorry I misunderstood - so your Button is inside the ListBox. In that case, you could try something like this:
<Button Command="{Binding DataContext.CancelCommand,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
This should reach out of the scope of the collection, looking in the DataContext of a Window, so if you have set the instance of your view model to the Window.DataContext, this should work.

TextBlock1 is not declared. It may be inaccessible due to its protection level

As silly as this sounds I'm a little stumped at this one. Here's my XAML in a Win Phone 8 App:
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel Grid.Row="0" Margin="12,17,0,28">
<TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock Text="Page" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<phone:LongListSelector x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="MainLongListSelector_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock x:Name="TextBlock1" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</Grid>
</Grid>
I've searched around but I don't know why I can't write code against the TextBlock1 control in code behind. When I type TextBlock1.Text= .... I get the error TextBlock1 is not declared. It may be inaccessible due to its protection level. But I can't see how it is private?
All I'm trying to do is add a textblock, assign some content to it, and then that selected value is passed across another page to perform relevant action.
In addition as soon as I remove it outside of the PhoneListSelector I can access it.
TextBlock1 is defined inside an ItemTemplate, anything defined a Template cannot be access directly as it will be created on runtime by the control.
You probably need to do binding on the TextBlock if you want to manipulate anything that the LongListSelector's DataContext has.
<phone:LongListSelector x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="MainLongListSelector_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock x:Name="TextBlock1" Text="{Binding Content"} HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Top"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
MainLongListSelector.DataContext = new List<TestViewModel>();
public class TestViewModel : INotifyPropertyChanged
{
//Assuming you've implemented the interface
private string _content;
public string Content { get { return _content; } { set { _content = value; NotifyOfPropertyChanged("Content"); } }
}
From here, you can try to access the selected value content and pass that to the next page.
var selectedItem = MainLongListSelector.SelectedItem as TestViewModel;
GoToNextPage(selectedItem.Content);
I strongly suggest to read MVVM design pattern and everything should be easy for you to implement, always remember UI is not DATA it's responsibility is only to show something that is passed through the ViewModel.

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