I want to be able to pass an enum parameter upon Loaded=" " so I can easily identify the section that is loading without having to do string trickery on the name.
My Expander XAML:
<Expander Loaded="ExpanderLoaded" x:Name="Greeting_And_Opening_Expander" ExpandDirection="Down" IsExpanded="True" FontSize="14" FontWeight="Bold" Margin="5" BorderThickness="1" BorderBrush="#FF3E3D3D">
The Method I want it to call:
private void ExpanderLoaded(object sender, RoutedEventArgs e, Sections section)
{
//Do stuff
}
My Enum (It will be significantly larger, this is just a test run):
public enum Sections
{
Default = 0,
Opening = 1,
Verify = 2
}
How do I go about passing the enum as a parameter upon Loading?
I would do this using EventTrigger and InvokeCommand action, that way in your view model ElementLoaded (For lack of a better name) gets called and the appropriate Enumeration gets passed in.
<Expander>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Loaded">
<i:InvokeCommandAction Command="{Binding ElementLoaded}"
CommandParameter="{x:Static local:Sections.Default}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Expander>
In your ViewModel, you will have a property of type ICommand called ElementLoaded, then in your constructor you initialize it as so
ElementLoaded = new ActionCommand(ElementLoadedMethod);
and the ElementLoadedMethod can be as so
private void ElementLoadedMethod(object section)
{
var sectionEnumVal = (Sections)section;
}
This should be all you have to do.
Related
I have this XAML code:
<DataGrid>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<prism:InvokeCommandAction Command="{Binding AddedSelectedClaimsCommand}" TriggerParameterPath="AddedItems" />
<prism:InvokeCommandAction Command="{Binding RemovedSelectedClaimsCommand}" TriggerParameterPath="RemovedItems" />
</i:EventTrigger>
<i:EventTrigger EventName="MouseDoubleClick">
<prism:InvokeCommandAction Command="{Binding ViewDetailsCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.ContextMenu>
<ContextMenu >
<MenuItem Header="View details" Command="{Binding ViewDetailsCommand}"/>
</ContextMenu>
</DataGrid.ContextMenu>
</DataGrid>
It works fine without the MouseDoubleClick EventTrigger. But when I added in the double click ability, the data grid suddenly appeared greyed out and rows could no longer be selected. Why?
Your canExecuteMethod delegate must be returning false for some reason, review your ViewModel and make sure all bellow is in place for you:
In you command initialization make sure you set canExecuteMethod delegate as well as your command action:
ViewDetailsCommand = new DelegateCommand(ExecuteViewDetailsCommand,
CanExecuteViewDetailsCommand);
Then goes your logic that verify if preconditions are met to execute this command. At the end it will enable or disable the associated control(s) for this command.
private bool CanExecuteViewDetailsCommand() {
return null != SelectedDetail;
}
If command successfully passed preconditions tests, then it can safely execute its method:
private void ExecuteViewDetailsCommand()
{
NavigateTo("DetailView",SelectedDetail);
}
You should have SelectedDetail property in place too (read/write):
Detail selectedDetail;
public Detail SelectedDetail
{
get { return selectedDetail; }
set {
SetProperty(ref selectedDetail, value);
RaiseCanExecuteEvents();
}
}
Note above RaiseCanExecuteEvents method invokation, this is a convenience method where you can force related commands validations:
protected virtual void RaiseCanExecuteEvents()
{
ViewDetailsCommand.RaiseCanExecuteChanged();
}
I've been working with WPF and I have experienced a problem related with DataTemplates.
I have a view called DetailPage.xaml and this view uses a DataTemplate called Detail.xaml. I added a textbox to this DataTemplate and I want to handle the TextChanged event. So I made something like this:
<DataTemplate x:Name="DetailContent">
<Grid Margin="5" DataContext="{Binding Items[0]}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition MaxHeight="80"/>
</Grid.RowDefinitions>
<StackPanel Width="432">
<TextBox Name="NumeroParadaTB" Text="{Binding NumeroParada}" MaxLength="5" TextChanged="NumeroParadaTB_TextChanged" />
</StackPanel>
</Grid>
</DataTemplate>
Then I created and event handler in DetailPage.xaml.cs, like the following:
protected async void NumeroParadaTB_TextChanged(object sender, TextChangedEventArgs e)
{
string nroParada = ((TextBox)sender).Text;
if(!string.IsNullOrEmpty(nroParada) && nroParada.IsDigitsOnly() && nroParada.Length == 5)
{
}
}
But when running, and error is thrown saying that the event handler doesn't exist. I guess I'm using the eventhandler in a wrong way.
Thanks!
Since you're using data binding, I assume, that you have some class with NumeroParada property:
public class SomeClass : INotifyPropertyChanged
{
/* other code here */
public string NumeroParada
{
get { return numeroParada; }
set
{
if (numeroParada != value)
{
numeroParada = value;
OnPropertyChanged("NumeroParada");
}
}
}
private string numeroParada;
}
Setter of this property will fire, when UI will update the binding source. This is your "TextChanged" event.
Note, that by default, TextBox updates Text property, when loosing focus. If you want to perform any action when user changes text, update your binding definition:
Text="{Binding NumeroParada, UpdateSourceTrigger=PropertyChanged}"
So far so good. But this code:
if(!string.IsNullOrEmpty(nroParada) && nroParada.IsDigitsOnly() && nroParada.Length == 5)
suggests, that you're trying to implement validation of value, entered by user.
Validation in WPF is rather big theme, I'd recommend you to read something like this to select validation approach.
Instead of adding an Event handler, you can use Event to Command logic. Create a Command in ViewModel and bind it to the TextChanged event.
<TextBox Text="{Binding SearchText, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding MyCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
Interaction triggers available in System.Windows.Interactivity assembly.
I have an existing solution of my WPF UI but it's ViewModel implementation is clunky and I'm looking to improve.
Below is a gif of how my current system works:
There's a Current Task (note: only ever one item)
There's a Task List for Tasks (note: possibly many) that need to run in the future
When the user selects one list box, the other selection is removed
The problem is, I'm implementing Current Task as a Listbox with only one item. This means I have to lug around a backing IList for the ItemSource and another property for the SelectedItem.
Is there another control I can use to behave like ListBoxItem, but I can bind my CurrentTask directly to it and not have to muck around with an List for ItemSource as well?
EDIT: To get the selection to go away when one listbox is selected, I have a trigger set up on the SelectionChanged event.
(deleted my previous answer)
It occurs to me that at least part of the functionality you're looking for is implemented by the RadioButton class. Multiple RadioButtons in the same scope guarantee that only one of them is selected. You'll probably have to do a little work to make sure that your RadioButtons can be scoped correctly in your UI, and you'll probably need to retemplate some things to get exactly the UI you need. Additionally, RadioButton does not have a SelectedItem/SelectValue property to which it can write to, because WPF provides no built-in mechanism for multiple controls to safely bind to a "SelectedWhatever" property. But you could roll this yourself pretty easily with codebehind or triggers.
Here's the implementation I went with:
XAML View
<!-- The Current Task box -->
<ListBox x:Name="CurrentTaskBox" FlowDirection="RightToLeft" Background="{StaticResource WhiteBrush}">
<ListBoxItem IsSelected="{Binding CurrentTaskSelected, Mode=TwoWay}" Content="{Binding CurrentTask.TaskId}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Selected">
<command:EventToCommand Command="{Binding SetTaskDetailsFromCurrentTaskCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBoxItem>
</ListBox>
<!-- The Task List box -->
<ListBox x:Name="TaskListBox" SelectedIndex="{Binding TaskListIndex}" SelectedValue="{Binding TaskListSelection}" IsSynchronizedWithCurrentItem="True" HorizontalContentAlignment="Stretch" ItemsSource="{Binding TaskList}" FlowDirection="RightToLeft" DisplayMemberPath="TaskId" Margin="3">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<command:EventToCommand Command="{Binding SetTaskDetailsFromTaskListCommand}" CommandParameter="{Binding SelectedItem, ElementName=TaskListBox}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ListBox>
ViewModel
/* Omitted most INPC property declarations...kinda boring */
public ICommand SetTaskDetailsFromCurrentTaskCommand { get { return new RelayCommand(SetTaskDetailsFromCurrentTask); } }
public ICommand SetTaskDetailsFromTaskListCommand { get { return new RelayCommand<TaskScheduleSequenceDto>(async taskSelection => await SetTaskDetailsFromTaskList(taskSelection)); } }
private bool _currentTaskSelected;
public bool CurrentTaskSelected
{
get
{
return _currentTaskSelected;
}
set
{
Set(() => CurrentTaskSelected, ref _currentTaskSelected, value);
}
}
private async Task SetTaskDetailsFromTaskList(TaskScheduleSequenceDto taskListSelection)
{
if (taskListSelection == null)
{
return;
}
var taskDetails = await _broker.RetrieveTaskDetails(taskListSelection.TaskId);
TaskDetails = taskDetails;
CurrentTaskSelected = false;
}
private void SetTaskDetailsFromCurrentTask()
{
TaskDetails = CurrentTask;
TaskListSelection = null;
CurrentTaskSelected = true;
}
This works fine and only requires that I have a single CurrentTask property in my VM, which I think is much cleaner.
I am using PRISM to develop my Windows Phone app using the MVVM design pattern. I need to pass my SelectedItem object from my LongListSelector through my delegate command into my method.
I can do that. The problem is, I'm passing in the wrong object. I don't know if it's a design problem or I am binding improperly.
I need the object to be an Album object. What I'm getting instead is either null or my ViewModel. (I've changed the code a few times and those are the only things I can get.)
XAML
<phone:LongListSelector x:Name="AlbumList" ItemsSource="{Binding Albums}"
Margin="10,0,0,0" LayoutMode="Grid" GridCellSize="200, 200"
ItemTemplate="{StaticResource AlbumTemplate}"
toolkit:TiltEffect.IsTiltEnabled="True"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<i:InvokeCommandAction Command="{Binding DataContext.SelectAlbumCommand, ElementName=ContentPanel}"
CommandParameter="{Binding}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</phone:LongListSelector>
ViewModel
private ObservableCollection<Album> _albums;
public ObservableCollection<Album> Albums
{
get { return _albums; }
set
{
if (value != null)
{
_albums = value;
NotifyPropertyChanged();
}
}
}
private Album _selectedAlbum;
public Album SelectedAlbum
{
get { return _selectedAlbum; }
// code removed as it is not needed; the object is null when trying to set.
}
public void AlbumSelected(object p)
{
App.Dispatcher.BeginInvoke(() =>
{
SelectedAlbum = (Album)p;
});
////Navigate("/Views/PhotosListPage.xaml");
}
//command that takes an object as parameter.
_selectAlbumCommand = new DelegateCommand<object>(this.AlbumSelected);
In case you merely want to set the SelectedAlbum by your SelectAlbumCommand, why don't you try binding the SelectedItem to SelectedAlbum instead?
<phone:LongListSelector x:Name="AlbumList" ItemsSource="{Binding Albums}"
SelectedItem="{Binding SelectedAlbum}" />
In case you actually want to pass the SelectedItem to the SelectedAlbumCommand (for some other reason), you should bind the CommandParameter to the SelectedItem of the LongListSelector
<i:InvokeCommandAction Command="{Binding DataContext.SelectAlbumCommand, ElementName=ContentPanel}" CommandParameter="{Binding ElementName=AlbumList, Path=SelectedItem}"/>
Apparently, you cannot use LongListSelector for this. I had to change this to a listbox and it worked fine.
Had I searched harder, I would have found this: How to select an item in LongListSelector using the MVVM-pattern?
and this: WP8 LongListSelector SelectedItem not bindable
What I am attempting to do is have a collection of items shown in a GridView control and have the size of these items change based on a command executed by a separate button.
For example, having a row of buttons across the top reading “Small”, “Medium” and “Large” and having the items in the GridView respond to the relevant command by displaying its items in the relevant state.
I have the gridview declared like so
<GridView ItemsSource="{Binding Squares}"
With Squares being an observable collection of Square objects that have a Title and a Fill property.
At first I went down the DataTemplateSelector route by declaring the following data templates in the Resources section of the page.
<DataTemplate x:Key="SquareSmallTemplate">
<Grid Height="100" Width="100">
<Rectangle Fill="{Binding Fill}"/>
<TextBlock Text="{Binding Title}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="SquareMediumTemplate">
<Grid Height="150" Width="150">
<Rectangle Fill="{Binding Fill}"/>
<TextBlock Text="{Binding Title}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="SquareLargeTemplate">
<Grid Height="200" Width="200">
<Rectangle Fill="{Binding Fill}"/>
<TextBlock Text="{Binding Title}"/>
</Grid>
</DataTemplate>
The idea being that the grid’s height and width properties are different for the relevant template. I declared the following data templates in the selector
public DataTemplate SmallTemplate { get; set; }
public DataTemplate MediumTemplate { get; set; }
public DataTemplate LargeTemplate { get; set; }
And in the SelecteTemplateCore method I just returned the relevant template
protected override DataTemplate SelectTemplateCore(object item, DependencyObject container)
{
string value = item as string;
if (value != null)
{
if (value == "Small")
return SmallTemplate;
else if (value == "Medium")
return MediumTemplate;
else if (value == "Large")
return LargeTemplate;
return base.SelectTemplate(item, container);
}
else
{
return base.SelectTemplateCore(item, container);
}
}
However, with this method (and, by design of the DataTemplateSelector) the object being passed in is the item in the collection (the Square).
This is fine if I wanted each item to have a different appearance or something, but what I need is the template to change based on another property on the view model.
For this, I have the following
public string State {get; set;}
and this is set to “Small”, “Medium, or “Large based on a separate row of three buttons that execute a command that sets this property to the relevant value.
How do I relate the State property to changing to the relevant DataTemplate?
Another route I tried was to have a single Data template that used the VSM to animate the Height/Width properties in the relevant states. However I could not get the relevant animation to execute when the State changed.
Any help would be great, thanks
There are a few ways to do this, I'm not sure which would be best. In any case, you'll need 1) a trigger, and 2) the action to update the template. I am leaning towards using PropertyChangedTrigger along with an InvokeCommandAction.
<GridView x:Name="grid">
<i:Interaction.Triggers>
<ei:PropertyChangedTrigger Binding="{Binding State}">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl},Path=UpdateTemplateCommand}" CommandParameter="{Binding State}" />
</ei:PropertyChangedTrigger>
</i:Interaction.Triggers>
<GridView>
(Here the "AncestorType" would just be the root of the view, so please change "UserControl" as needed.)
Then in the view, you would have an ICommand that updates the template:
UpdateTemplateCommand = new DelegateCommand(state => {
switch ((string)state)
{
default:
case "Small" : grid.ItemTemplate = "SquareSmallTemplate"; break;
case "Medium" : grid.ItemTemplate = "SquareMediumTemplate"; break;
case "Large" : grid.ItemTemplate = "SquareLargeTemplate"; break;
}
});
IDK ... after writing this out it seems a bit convoluted. Maybe you'd find it preferable to add a CurrentDataTemplate property to the view-model, and assign it by creating DataTemplates from strings using XamlReader.