I would like to handle the Checked and Unchecked events of a Checkbox control and execute a command in my ViewModel. I wired up an EventTrigger for both the Checked and Unchecked events as follows:
<CheckBox x:Name="chkIsExtendedHr" IsChecked="{Binding Schedule.Is24Hour, Mode=TwoWay}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<GalaSoft_MvvmLight_Command:EventToCommand
CommandParameter="{Binding IsChecked, ElementName=chkIsExtendedHr}"
Command="{Binding Path=SetCloseTime, Mode=OneWay}" />
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<GalaSoft_MvvmLight_Command:EventToCommand
CommandParameter="{Binding IsChecked, ElementName=chkIsExtendedHr}"
Command="{Binding Path=SetCloseTime, Mode=OneWay}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
I defined a RelayCommand in my ViewModel and wired up an action for it:
public RelayCommand<Boolean> SetCloseTime{ get; private set; }
...
SetCloseTime= new RelayCommand<bool>(ExecuteSetCloseTime);
The parameter in the action for the command always resolves to the previous state of the CheckBox, e.g. false when the CheckBox is checked, and true when the CheckBox is unchecked.
void ExecuteSetCloseTime(bool isChecked)
{
if (isChecked)
{
// do something
}
}
Is this expected behavior?
I have a workaround where I have separate triggers (and commands) for the Checked and Unchecked and use a RelayCommand instead of RelayCommand<bool>. Each command executes correctly when the CheckBox is checked and unchecked. Feels a little dirty though - even dirtier than having UI code in my ViewModel :)
Thanks
I think using "Click" event instead of "Checked" or "UnChecked" can solve this problem with just one command and no additional code.
In XAML it will look like,
<i:EventTrigger EventName="Click">
<GalaSoft_MvvmLight_Command:EventToCommand CommandParameter="{Binding IsChecked, ElementName=chkIsExtendedHr}" Command="{Binding Path=SetCloseTime, Mode=OneWay}" />
</i:EventTrigger>
now rest of you code should work the you wanted,
thanks,
Why don't you handle your actions in your Schedule.Is24Hour. In setter you always can see when that property is changed.
i do this for checking checkbox
for view
<CheckBox Margin="126,0,0,0" IsChecked="{Binding UseNOCODE, Mode=TwoWay}" Content="Reply Messages ?" />
for the modelview
private bool _useNOCODE = false;
public bool UseNOCODE
{
get
{
return _useNOCODE;
}
set
{
if (_useNOCODE == value)
{
return;
}
_useNOCODE = value;
RaisePropertyChanged("UseNOCODE");
UseNoCodeChecked();
}
}
private void UseNoCodeChecked()
{//check the properties and what you like}
Related
In my Xmal I have
<Button
Command = "{Binding FaultClick}"
/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<ItemsControl ItemsSource="{Binding FaultButtons}"
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button command={"Binding FaultCheck"}>
<Grid>
<TextBlock Text={"Binding FaultButtons.Content"}/}
</Grid>
</Button>
</DataTemplate>
</ItemsControl,ItemTemplate>
<ItemsControl>
and in my ViewModel I have
FaultCheck = new RelayCommand(ClickThisFault,() => true);
in the constructor
Public RelayCommand FaultCheck
{
Get;
Private set;
}
public void ClickThisFault()
{
some actions
}
in the body
What I am trying to achieve is to dynamically build a set of buttons that that the user can click to register faults.
the command binding on the button outside the ItemsControl works fine, I put it there to test the binding.
the itemsource binding on the ItemsControl works as well, my dynamic buttons are created but the Command Binding on the Buttons inside the ItemsControl and the Text Binding on the textBlock dosent work.
Am I missing something with Binding Dynamically created objects ?? or is there a better way to do this??
I'm based on the official Microsoft sample to create a MasterDetail ListView:
MasterDetail ListView UWP sample
I have adapted it to my case, as I want that users can edit directly selected items from the ListView. But I meet a strange comportement:
when I add a new item to the ListView, the changes of the current item, done in the details container, are well saved
but when I select an existing item in the ListView, the changes of the current item, done in the details container, are not saved
Here is a screenshot of my app:
The XAML of my ListView is like this:
<!-- Master : List of Feedbacks -->
<ListView
x:Name="MasterListViewFeedbacks"
Grid.Row="1"
ItemContainerTransitions="{x:Null}"
ItemTemplate="{StaticResource MasterListViewFeedbacksItemTemplate}"
IsItemClickEnabled="True"
ItemsSource="{Binding CarForm.feedback_comments}"
SelectedItem="{Binding SelectedFeedback, Mode=TwoWay}">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
</Style>
</ListView.ItemContainerStyle>
<ListView.FooterTemplate>
<DataTemplate>
<CommandBar Background="White">
<CommandBar.Content>
<StackPanel Orientation="Horizontal">
<AppBarButton Icon="Add" Label="Add Feedback"
Command="{Binding AddItemFeedbacksCommand}" />
<AppBarButton Icon="Delete" Label="Delete Feedback"
Command="{Binding RemoveItemFeedbacksCommand}" />
</StackPanel>
</CommandBar.Content>
</CommandBar>
</DataTemplate>
</ListView.FooterTemplate>
</ListView>
The XAML of the ListView's ItemTemplate is:
<DataTemplate x:Key="MasterListViewFeedbacksItemTemplate" x:DataType="models:Feedback_Comments">
<StackPanel Margin="0,11,0,13"
Orientation="Horizontal">
<TextBlock Text="{x:Bind creator }"
Style="{ThemeResource BaseTextBlockStyle}" />
<TextBlock Text=" - " />
<TextBlock Text="{x:Bind comment_date }"
Margin="12,1,0,0" />
</StackPanel>
</DataTemplate>
The XAML of the Details container is like this:
<!-- Detail : Selected Feedback -->
<ContentPresenter
x:Name="DetailFeedbackContentPresenter"
Grid.Column="1"
Grid.RowSpan="2"
BorderThickness="1,0,0,0"
Padding="24,0"
BorderBrush="{ThemeResource SystemControlForegroundBaseLowBrush}"
Content="{x:Bind MasterListViewFeedbacks.SelectedItem, Mode=OneWay}">
<ContentPresenter.ContentTemplate>
<DataTemplate x:DataType="models:Feedback_Comments">
<StackPanel Visibility="{Binding FeedbacksCnt, Converter={StaticResource CountToVisibilityConverter}}">
<TextBox Text="{Binding creator, Mode=TwoWay}" />
<DatePicker Date="{Binding comment_date, Converter={StaticResource DateTimeToDateTimeOffsetConverter}, Mode=TwoWay}"/>
<TextBox TextWrapping="Wrap" AcceptsReturn="True" IsSpellCheckEnabled="True"
Text="{Binding comment, Mode=TwoWay}" />
</StackPanel>
</DataTemplate>
</ContentPresenter.ContentTemplate>
<ContentPresenter.ContentTransitions>
<!-- Empty by default. See MasterListView_ItemClick -->
<TransitionCollection />
</ContentPresenter.ContentTransitions>
</ContentPresenter>
The "CarForm" is the main object of my ViewModel. Each CarForm contains a List of "Feedback_Comments".
So in my ViewModel, I do this when I add a new comment:
private void AddItemFeedbacks()
{
FeedbacksCnt++;
CarForm.feedback_comments.Add(new Feedback_Comments()
{
sequence = FeedbacksCnt,
creator_id = user_id,
_creator = username,
comment_date = DateTime.Now
});
SelectedFeedback = CarForm.feedback_comments[CarForm.feedback_comments.Count - 1];
}
=> the changes done in the Feedback_Comment that was edited before the add are well preserved
I don't do anything when the user select an existing Feedback_Comment: this is managed by the XAML directly.
=> the changes done in the Feedback_Comment that was edited before to select anoter one are not preserved
=> Would you have any explanation?
The TwoWay binding for the Text property is updated only when the TextBox loses focus. However, when you select a different item in the list, the contents of the TextBox are no longer bound to the original item and so are not updated.
To trigger the update each time the Text contents change, so that the changes are reflected immediately, set the UpdateSourceTrigger set to PropertyChanged:
<TextBox Text="{Binding comment, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Triggering changes everywhere
To ensure your changes are relflected everywhere including the list, you will need to do two things.
First, your feedback_comments is of type ObservableCollection<Feedback_Comments>. This ensures that the added and removed items are added and removed from the ListView.
Second, the Feedback_Comments class must implement the INotifyPropertyChanged interface. This interface is required to let the user interface know about changes in the data-bound object properties.
Implementing this interface is fairly straightforward and is described for example on MSDN.
The quick solution looks like this:
public class Feedback_Comments : INotifyPropertyChanged
{
// your code
//INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged( [ CallerMemberName ]string propertyName = "" )
{
PropertyChanged?.Invoke( this, new PropertyChangedEventArgs( propertyName ) );
}
}
Now from each of your property setters call OnPropertyChanged(); after setting the value:
private string _comment = "";
public string Comment
{
get
{
return _comment;
}
set
{
_comment = value;
OnPropertyChanged();
}
}
Note, that the [CallerMemberName] attribute tells the compiler to replace the parameter by the name of the caller - in this case the name of the property, which is exactly what you need.
Also note, that you can't use simple auto-properties in this case (because you need to call the OnPropertyChanged method.
Bonus
Finally as a small recommendation, I see you are using C++-like naming conventions, which does not fit too well into the C# world. Take a look at the recommended C# naming conventions to improve the code readability :-) .
I may be approaching this all wrong, so tell me if you have an alternative suggestions.
I'm making an app for windows RT that will have a bunch of text blocks displayed to the user, for example character stats.
The user will see:
Str: 10
Con: 10
Dex: 10
and so on.
I want them to be able to fill these in, then have a select view values calculated based on the result.
My though was to click an "Edit" button at the top and toggle some text boxes over each editable text block.
When trying to set this up using "Blend for Visual Studio" I can't seem to make a text box that is smaller than 49x34 (much larger than my text blocks).
I was going to find a way to generate a text box for each text block (using its dimensions) on button click, but since they will always be the same and there will be a lot of them I was trying to make them static via blend.
I'm pretty new to XAML, and I can't seem to find a good example of people setting up editable fields like this, so how should I make a bunch of static fields have editable text boxes?
I would create both the TextBox and TextBlock overlays in XAML, and place them directly on top of each other in a Grid, using Horizontal and Vertical alignments to "Center" to ensure that the text is always completely lined up. I would also use static Widths to ensure that the columns line up well.
From there, you can directly bind the Visibility to some boolean "IsEditing" property, to make sure that only one of the controls are shown at a time.
<StackPanel Orientation="Horizontal">
<TextBlock Text="Str: " Width="40" VerticalAlignment="Center" />
<Grid Width="40" VerticalAlignment="Center">
<TextBlock Text="{Binding Strength}"
Visibility="{Binding IsEditing, Converter={StaticResource BooleanToInvisibilityConverter}}"
VerticalAlignment="Center"
HorizontalAlignment="Center" />
<TextBox Text="{Binding Strength}"
Visibility="{Binding IsEditing, Converter={StaticResource BooleanToVisibilityConverter}}"
VerticalAlignment="Center"
HorizontalContentAlignment="Center" />
</Grid>
</StackPanel>
Somewhere along the way you'll have to define your "BooleanToVisibility" and "BooleanToInvisiblity" converter resources. I like this implementation by Diedrik Krols. It's nice and simple, with the option to invert.
You might want to use a style for a TextBox, which changes depending on whether or not the "IsReadOnly" property is true or not.
When IsReadOnly is true, you can set the BorderBrush and Background to Transparent, thus making it look like a normal textblock.
In this way, you don't have to overlay TextBlocks and TextBoxes; just use TextBox controls by themselves, and toggle the "IsReadOnly" property when you click the Edit button.
In your resources:
<Style x:Key="MyEditableField" TargetType={x:Type TextBox}>
<Style.Triggers>
<DataTrigger Binding="{Binding IsReadOnly, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="Background" Value="Transparent" />
</DataTrigger>
</Style.Triggers>
</Style>
And here's one of your editable fields:
<StackPanel Orientation="Horizontal">
<TextBlock Text="Str: " />
<TextBox Style="{StaticResource MyEditableField}"
Text="{Binding Strength}"
IsReadOnly="{Binding IsEditingDisabled}" />
</StackPanel>
Late answer, but who wants can also create a custom editable textbox, its pretty easy actually here is the code (obviously you can modify it for your own needs)
public class EditableTextBox : TextBox
{
public EditableTextBox()
{
this.BorderBrush = new SolidColorBrush(Colors.Black);
}
protected override void OnTapped(TappedRoutedEventArgs e)
{
this.IsReadOnly = false;
SetEditingStyle();
base.OnTapped(e);
}
protected override void OnDoubleTapped(DoubleTappedRoutedEventArgs e)
{
this.IsReadOnly = false;
SetEditingStyle();
base.OnDoubleTapped(e);
}
protected override void OnLostFocus(RoutedEventArgs e)
{
this.IsReadOnly = true;
SetReadonlyStyle();
base.OnLostFocus(e);
}
public void SetReadonlyStyle()
{
this.BorderBrush.Opacity = 0;
this.Background.Opacity = 0;
}
public void SetEditingStyle()
{
this.BorderBrush.Opacity = 1;
this.Background.Opacity = 1;
}
}
Sample:
Tutorial: Full tutorial url
Using a property to toggle edit mode between view AND viewmodel is a bad design approach you should use events and command binding to communicate changes of states like this between view and viewmodel.
Here is an article that describes the principle in an MVVM compliant way:
http://www.codeproject.com/Articles/802385/A-WPF-MVVM-In-Place-Edit-TextBox-Control
Please have a look and tell me what you think.
This builds off of BTownTKD's solution, but as I really do prefer as much WPF of a solution as possible here is a bit of a modification, in my case I'm trying to modify the name of a tab control.
My view model has the following code:
private bool _isEditingName = false;
public bool IsEditingName
{
get
{
return _isEditingName;
}
set
{
_isEditingName = value;
OnPropertyChanged();
}
}
public ICommand StartEditing
{
get
{
return new DelegateCommand(() =>
{
IsEditingName = true;
});
}
}
public ICommand EndEditing
{
get
{
return new DelegateCommand(() =>
{
IsEditingName = false;
});
}
}
Next is my view that has the data template for the tab (not the content just the tab):
<TabControl ItemsSource="{Binding Items}" SelectedItem="{Binding ActiveItem}">
<TabControl.ItemTemplate>
<DataTemplate>
<Grid VerticalAlignment="Center">
<TextBlock x:Name="TabName" Text="{Binding Name}" Visibility="{Binding IsEditingName, Converter={StaticResource InvertedBoolToVisConverter}}" VerticalAlignment="Center" HorizontalAlignment="Stretch" TextAlignment="Left">
<TextBlock.InputBindings>
<MouseBinding MouseAction="LeftDoubleClick" Command="{Binding StartEditing}" />
</TextBlock.InputBindings>
</TextBlock>
<TextBox Text="{Binding Name}" Visibility="{Binding IsEditingName, Converter={StaticResource BoolToVisConverter}}" VerticalAlignment="Center" HorizontalContentAlignment="Stretch" TextAlignment="Left" IsVisibleChanged="TextBox_IsVisibleChanged">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<i:InvokeCommandAction Command="{Binding EndEditing}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding EndEditing}" />
</TextBox.InputBindings>
</TextBox>
</Grid>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
And last but not least, I wanted a double click to put me in edit mode, and to auto focus on the textbox and select all of the content for immediate typing. None of the xaml solutions were as clean as a simple code behind so I finally just decided on adding this to the textbox on visibility changed handler:
private void TextBox_IsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
var box = sender as TextBox;
if (box != null)
{
if ((bool)e.NewValue)
{
box.Focus();
box.SelectAll();
}
}
}
Out of all of the solutions I found, this was by far my favorite. Thanks everyone for your posts!! Helped me find a really good overall solution to my problem!
I am using Listbox and it contains button ,and i want to handle button click event using command.but my command never calls.
is this Correct way??
<pmControls:pmListBox Grid.Row="1" Margin="3" ItemsSource="{Binding Countries}" SelectedItem="{Binding SelectedCountry}" >
<pmControls:pmListBox.ItemTemplate >
<DataTemplate >
<Button Command="{Binding GetAllStatesCommand}" CommandParameter="{Binding}" Margin="3" Width="100" Height="50" Content="{Binding Title}">
</Button>
</DataTemplate>
</pmControls:pmListBox.ItemTemplate>
</pmControls:pmListBox>
The DataContext of one list item is different from the DataContextof the surrounding control. To bind that command to the DataContext of that control you have two options:
Either you provide the control with a name and reference to that:
<pmControls:pmListBox x:Name="myCoolListBox" [...]>
<pmControls:pmListBox.ItemTemplate>
<DataTemplate>
<Button Command="{Binding DataContext.GetAllStatesCommand, ElementName=myCoolListBox}" CommandParameter="{Binding}" [...] />
</DataTemplate>
</pmControls:pmListBox.ItemTemplate>
</pmControls:pmListBox>
Or you create class holding your DataContext...
public class DataContextBinder : DependencyObject
{
public static readonly DependencyProperty ContextProperty = DependencyProperty.Register("Context", typeof(object), typeof(DataContextBinder), new PropertyMetadata(null));
public object Context
{
get { return GetValue(ContextProperty); }
set { SetValue(ContextProperty, value); }
}
}
...and create an instance of that in the resources section of your ListBox:
<pmControls:pmListBox x:Name="myCoolListBox" [...]>
<pmControls:pmListBox.Resources>
<local:DataContextBinder x:Key="dataContextBinder" Context="{Binding}" />
</pmControls:pmListBox.Resources>
<pmControls:pmListBox.ItemTemplate>
<DataTemplate>
<Button Command="{Binding Context.GetAllStatesCommand, Source={StaticResource dataContextBinder}" CommandParameter="{Binding}" [...] />
</DataTemplate>
</pmControls:pmListBox.ItemTemplate>
</pmControls:pmListBox>
How to use trigger event in a button inside a datagrid in silverlight using relaycommand mvvm
Iam unable to get selected values in to some Dto , it means once i selected a row for delete , the selected item property showing NULL .how to solve it pls
You can use trigger event like below in datagrid:
<Button Content="Message" Height="23" HorizontalAlignment="Left" Margin="234,116,0,0" Name="btnMsg" VerticalAlignment="Top" Width="75" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<si:CallDataMethod Method="HandleShowMessage"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
You have to add necessary reference for this.
For selecteditem you have to set selected item into datagrid and other thing you have to decalre a property in viewmodel:
In Xaml:
<sdk:DataGrid Height="Auto" AutoGenerateColumns="False" ItemsSource="{Binding Emp}" SelectedItem="{Binding SelectedEMp,Mode=TwoWay}" BorderThickness="1" HorizontalAlignment="Left" Name="dataGrid1" VerticalAlignment="Top" Width="auto">
and in Viewmodel:
private EmpInfo _selectedEMp;
public EmpInfo SelectedEMp
{
get { return _selectedEMp; }
set
{
_selectedEMp = value;
on("SelectedEMp");
}
}
Thanks