I'm creating a WPF app using MVVMLight.
I defined a ListView inside a TabControl DataTemplate, like so:
<TabControl.ContentTemplate>
<DataTemplate>
<ListView ItemsSource="{Binding Builds}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
SelectedItem="{Binding SelectedBuild,
Mode=TwoWay}"
SelectionMode="Single">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDoubleClick">
<i:InvokeCommandAction Command="{Binding BuildSelectedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListView>
</DataTemplate>
</TabControl.ContentTemplate>
but XAML Designer returns this error (preventing the load of the designer preview):
XamlObjectWriterException: Collection property 'System.Windows.Controls.ListView'.'Triggers' is null.
at System.Xaml.XamlObjectWriter.WriteGetObject()
at System.Xaml.XamlWriter.WriteNode(XamlReader reader)
at System.Windows.FrameworkTemplate.LoadTemplateXaml(XamlReader templateReader, XamlObjectWriter currentWriter)
my Command is defined like so in my ViewModel:
private RelayCommand _buildSelectedCommand;
public RelayCommand BuildSelectedCommand => _buildSelectedCommand ??
(_buildSelectedCommand = new RelayCommand(BuildSelectedAction));
This is the first time I'm seeing this error, and it's happening only at design time, building and runtime it's fine.
Removing the i:Interaction.Triggers fix the problem, but I need the DoubleClick event on the list.
If you ask why I didn't add the trigger at the ListItem level, it's because I have to set a property on the ViewModel binding the TabControl Datatemplate, not the ListItem ViewModel.
Thanks a lot for the help!
I just verified that this is a Visual Studio 2015 bug, it doesn't repro in Visual Studio 2017 RC.
Related
I have been searching for the DisconnectedItem issue over the Internet and I have learned that it should have been a solved problem in 4.5 release of .NET Framework. I am currently using 4.5.1 and it's my first time to face this kind of malfunction. The scenario is I have a ViewModel and a View. After an entity changed event (nHibernate) I dispose the old ViewModel, create a new one of the same type and attach it to already existing View (resolved form the Unity container). The problem is that the View has an ItemsControl with ItemsSource bound to the ViewModel List. After attaching the new ViewModel, the DelegateCommand refreshes the CanExecute methods. At first I get the objects, but than comes a sequence of DisconnectedItems (so even though I return false if DisconnectedItem, I'd get all buttons blocked). Is there any other way of changing the DataContext without creating a brand new View?
Here is the "after entity changed" part. The reportFilesCollection is bound to ItemsControl:
_detailsViewModel.Dispose();
_detailsViewModel = new DetailsViewModel(reportFilesCollection, _unityContainer);
IDetailsView view = GetViewOfTypeFromRegion<IDetailsView>();
view.ViewModel = _detailsPreviewViewModel;
And the View part:
<StackPanel x:Name="reportDataPart">
<ItemsControl ItemsSource="{Binding ReportFiles}"
Style="{StaticResource IconDataTableStyle}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<ItemsControl Style="{StaticResource IconDataStyle}"
Tag="PrinterPath">
<Label ToolTip="Report generation time"
Content="{Binding CreatedOn, StringFormat={}{0:g}}"/>
<Button Content="Sign report"
Command="{Binding DataContext.SignReportCommand, ElementName=reportDataPart}"
CommandParameter="{Binding RelativeSource={RelativeSource Self}, Path=DataContext}"/>
</ItemsControl>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
The second ItemsControl (the inner one) provides a special style for the row of data. The absence of it doesn't make any difference. After I reenter the View, all of the buttons are active again but DisconnectedItems are still being present.
I am using Caliburn Micro for a Windows Phone app. I have a hyperlink control for which I want to bind the click event to my View Model. Below is the sample code
XAML, MyPage.xaml
<TextBlock>
<Run>Got to</Run>
<Hyperlink micro:Message.Attach="[Event Click] = [Action OpenAnotherPage]">
My Page</Hyperlink><Run Text="."></Run></TextBlock>
ViewModel MyPageViewModel.cs
public void OpenAnotherPage()
{
// some code
}
When I tap on the link, I get an exception
System.Exception: No target found for method
What could be the problem?
Update 1: Tried setting micro:Action.TargetWithoutContext="{Binding ElementName=MyPage, Path=DataContext}" on the Hyperlink control, but it didn't work
Give the TextBlock a name and use that as the ElementName since you have the Hyperlink nested into that control. Then Update1 should work.
Try following XAML (untested):
<Hyperlink>My Page
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<ec:CallMethodAction TargetObject="{Binding}" MethodName="OpenAnotherPage" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Hyperlink>
The namespaces are following (you'll need to reference those 2 assemblies, they're by Microsoft and shipped with MS Expression Blend)
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ec="clr-namespace:Microsoft.Expression.Interactivity.Core;assembly=Microsoft.Expression.Interactions"
I'm trying to add a Blend behavior to a DatePicker control to bind an MVVM-Light RelayCommand to the DateChanged event like so:
<DatePicker Date="{Binding SelectedDate, Mode=TwoWay}">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="DateChanged">
<Core:InvokeCommandAction Command="{Binding DateChangedCommand, Mode=OneWay}"/>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</DatePicker>
I'm using the following definitions:
xmlns:Interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:Core="using:Microsoft.Xaml.Interactions.Core"
However, I keep getting the following error:
WinRT information: Cannot add instance of type
'Microsoft.Xaml.Interactions.Core.EventTriggerBehavior' to a collection of type
'Microsoft.Xaml.Interactivity.BehaviorCollection'.
I have successfully used Blend behaviors in this way many other times in my project (and with other controls in the same Xaml file) to bind an event to a command, and the DatePicker control is the only one that has thrown an error. Is there another way to accomplish this or are WinRT DatePickers limited in this way?
I have a doubt about binding to an ancestor property in WPF. My situation is: my window has a view model as data context with all the commands as ICommand properties. I have a list of checkboxes as follows:
<ItemsControl ItemsSource="{Binding CurrentCustomer.SuppiersSelection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox FontSize="16" Content="{Binding Path=Supplier.Company}"
IsChecked="{Binding Path=Selected}"></CheckBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The question is: I want that each checkbox has a command bound to it, so that when a supplier is checked or unchecked the relationship between customer and supplier is automatically saved. My problem is that the command properties are on the Window Data Context and this checkboxes is using as data context the ItemsSource of ItemControl. How do I bind to the property on the Window Data Context?
Thanks in advance for your help.
{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=Window}, Path=DataContext}
But it's not clear what is the task.
Why don't you use two way binding and process the change of property Selected?
Let's say I currently have an ItemsControl whose DataTemplate is a bunch of buttons. I'm wiring up these buttons' click events, but how am I to know which button was clicked? Should I not use a ItemsControl?
I'm trying to have no code-behind, but being pragmatic may be necessary.
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Margin="10">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<GalaSoft_MvvmLight_Command:EventToCommand Command="{Binding ItemsControlButtonClicked, Mode=OneWay}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
If you want to know what Item was clicked, then pass {Binding } as the CommandParameter and it will pass the selected object to your Command
If you want to know what Button was clicked, I would do that in the code-behind since ViewModels do not need to know anything about the UI, and that includes buttons.
Also since your control is a Button, you should use the Command property instead of a Click trigger.
<Button Command="{Binding ItemsControlButtonClicked}" />
You can send parameters along with the command and based on these parameters you can find out which button was clicked
In my project I also use the MVVM Light I has an dropdown with collection of items, and a button which user press and action depend on selected item from drop down
you should create a Relay command with parameter look at the example from my code
public RelayCommand<Project> StartTimer { get; private set; }//declare command
StartTimer = new RelayCommand<Project>(OnStartTimer);
private void OnStartTimer(Project project)
{
if (project != null)
{
currentProject = project;
if (!timer.IsTimerStopped)
{
timer.StopTimer();
}
else
{
Caption = "Stop";
timer.StartTimer();
}
}
on the view I bind the drop down with collection of class Project
and for button command parameter I bind the selected item form drop down
look at the code
<ComboBox Name="projectcomboBox" ItemsSource="{Binding Path=Projects}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="FullName"
SelectedValuePath="Name" SelectedIndex="0" >
</ComboBox>
<Button Name="timerButton" Content="{Binding Path=Caption}" Command="{Binding Path=StartTimer}"
CommandParameter="{Binding ElementName=projectcomboBox, Path=SelectedItem}" ></Button>
pay attention to Command and CommandParameter binding
also you can use this approache not only for drop down
Well, you can use the Sender.DataContext which is the actual data.
Create command properties in your view model class (using Josh Smith's RelayCommand pattern is the simplest way to do this) and bind each button's Command to the appropriate one. Not only is this straightforward to do and simple to maintain, it also gives you an easy way of implementing the enable/disable behavior when you need to.