Before my page load I check some data for the state of the ToggleButton:
private void CheckVoteState()
{
var getVoteState = from state in SelectedActivity.UserVotes
select state.Type;
string voteState = getVoteState.FirstOrDefault();
if (voteState == "positive")
{
VoteStateText = LocalizedStrings.Get("VoteStateUnLikeText");
VoteState = true;
}
else
{
VoteStateText = LocalizedStrings.Get("VoteStateLikeText");
VoteState = false;
}
}
But I have this 2 commands, and ofcours when my page loads one of this 2 commands triggers:
public RelayCommand LikeCommand
{
get
{
return new RelayCommand(async() =>
{
//Call Like Api
await _IDataService.VoteForProposition(_SelectedActivity.Object.Id);
});
}
}
public RelayCommand UnLikeCommand
{
get
{
return new RelayCommand(async() =>
{
//Call Unlike Api
await _IDataService.RemoveVoteOnProposition(_SelectedActivity.Object.Id);
});
}
}
I don't want them to get triggerd when the pages loads, but only when the user checks or unchecks.
This is my xaml side:
<ToggleButton Content="{Binding VoteStateText}"
Foreground="White"
Background="LightGray"
Margin="10,-10,10,0"
BorderThickness="0"
HorizontalAlignment="Stretch"
IsChecked="{Binding VoteState}">
<i:Interaction.Behaviors>
<core:EventTriggerBehavior EventName="Checked">
<core:InvokeCommandAction Command="{Binding LikeCommand}" />
</core:EventTriggerBehavior>
<core:EventTriggerBehavior EventName="Unchecked">
<core:InvokeCommandAction Command="{Binding UnLikeCommand}" />
</core:EventTriggerBehavior>
</i:Interaction.Behaviors>
</ToggleButton>
In the past I solved this with _isPageLoaded boolean. Simply set it to true in Page_Loaded event handler and check in your commands.
Related
I have a datagridview populated with items and I am using a SelectionChanged event to populate textboxes from that data when selected.
If I make a selection, everything works. If I click elsewhere in the App and then come back to click the SelectionChanged event again on the same item - it doesn't work.
According to MSDN:
"This event occurs whenever there is a change to a selection."
MSDN SelectionChangedEvent
So it appears that despite clicking elsewhere, resetting the Textboxes - the selected item is not changing as the SelectionChanged event no longer triggers - click on another item and it works, click back again and it works - but click on it, reset textboxes, click it again - nothing happens, this includes clicking in the datagridview itself in a blank area.
XAML:
<DataGrid x:Name="TimeView" Grid.Row="1" Grid.Column="3"
Grid.ColumnSpan="3" Grid.RowSpan="4" Margin="10 50 10 10"
CanUserAddRows="False" Visibility="{Binding StartTiming}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cal:ActionMessage MethodName="SelectedTimeChangeEvent">
<cal:Parameter Value="$eventArgs" />
</cal:ActionMessage>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
ViewModel
public void SelectedTimeChangeEvent(SelectionChangedEventArgs e)
{
foreach (TimeData addedRow in e.AddedItems)
{
TbID = addedRow.ID;
TbDate = addedRow.Date;
TbStartTime = addedRow.StartTime;
TbDescription = addedRow.Description;
}
}
Since I am using MVVM and Caliburn, TimeView is connected to an ICollection, which is in turn connected to an ObservableCollection:
private ObservableCollection<TimeData>? _timeCollection;
public ObservableCollection<TimeData>? TimeCollection
{
get { return _timeCollection; }
set
{
_timeCollection = value;
NotifyOfPropertyChange(() => TimeCollection);
}
}
private ICollectionView? _timeView;
public ICollectionView? TimeView
{
get { return _timeView; }
set
{
_timeView = value;
NotifyOfPropertyChange(() => TimeView);
}
}
There is a work around, which is the following after populating the Textboxes:
TimeView = null;
TimeView = CollectionViewSource.GetDefaultView(TimeCollection);
This works, but I thought that there might be a "deselect" option that would be better than repopulating every time a selection is made, one of my Datagrids contains 15,000 items, and it is still instant, but seems overkill to populate it every time a selection is made.
i would recommend bindings, they automaticly reset when nothing is selected
<DockPanel>
<StackPanel DataContext="{Binding SelectedTime}" DockPanel.Dock="Left">
<TextBlock Text="{Binding ID}"/>
<TextBlock Text="{Binding Date}"/>
<TextBlock Text="{Binding StartTime}"/>
<TextBlock Text="{Binding Description}"/>
</StackPanel>
<DataGrid ItemsSource="{Binding TimeView}" SelectedItem="{Binding SelectedTime}">
...
</DataGrid>
</DockPanel>
public TimeData SelectedTime
{
get { return _selectedTime; }
set
{
_selectedTime = value;
NotifyOfPropertyChange(() => SelectedTime);
}
}
also there is this neet feature
protected virtual void SetValue<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
field = value;
OnPropertyChanged(propertyName);
}
so you can write
set { SetValue(ref _selectedTime, value) }
I am currently trying to prevent a user from inputting anything aside from numbers into a textbox.
I currently have a Textbox with a textchange event which calls a relay command. however I am not able to effect the textbox from the relay command.
I have tried directly settings the textbox.text and that has not worked, nor has trying to return the string.
Here is my code
XAML
<TextBox x:Name="TextBox" Margin="5,5,0,0" Grid.Column="1" Grid.Row="3" HorizontalAlignment="Left" Width="31" ">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<command:EventToCommand Command="{Binding TextBoxTextChanged,Mode=TwoWay}"
CommandParameter="{Binding Path=Text, ElementName=TextBox}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
View Model
DaysBackTextChanged = new RelayCommand<string>(daysBackText =>
{
Func<string> function = () =>
{
if (!Regex.IsMatch(daysBackText, "[^0-9]+"))
{
//return TextBox.Text;
return "X";
}
else
{
return "Y";
}
};
});
Modify your XAML to add a PreviewTextInput event in the TextBox
PreviewTextInput="NumbersOnly"
Now,let's use Regex,shall we ?
using System.Text.RegularExpressions;
private void NumbersOnly(object sender, TextCompositionEventArgs e)
{
Regex regex = new Regex("[^0-9]+");
e.Handled = regex.IsMatch(e.Text);
}
Here e.Handeled is to prevent anything rather than numbers being typed.
Hope this works :)
After extensive researching I have not found an answer to this problem. I have a list box whose ItemsSource is a collection of Button objects. When I add a button to the collection it appears properly but when clicked the command is not executed. I have already implemented RelayCommand and it is used throughout my code.
C# MVVM WPF
The View
<ListBox ItemsSource="{Binding Buttons}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<Button Margin="5,5,5,5"
Content="{Binding Content}"
Command="{Binding ExecuteButtonCommand}"
CommandParameter="{Binding CommandParameter}"
/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The ViewModel
public RelayCommand _executeButtonCommand;
public ICommand ExecuteButtonCommand
{
get
{
if (_executeButtonCommand == null)
_executeButtonCommand = new RelayCommand(exec => this.ButtonCommands(param));
return _executeButtonCommand;
}
}
For Testing I have this code.
public void AddButtons()
{
Buttons= new ObservableCollection<Button>();
Button btn = new Button();
btn.Content = "Generate Files";
btn.Command = "{Binding ExecuteButtonCommand}";
btn.CommandParameter = "Files";
Buttons.Add(btn);
}
But I cannot assign the Command that way. The rest of the button works correctly. So I put the Command= in the view as you see above.
If this has been answered, then I can't find it. The nearest answer is nine years old and does not work.
Thanks for looking.
What is happening is that the ListBox's DataTemplate is trying to bind to a property called ExecuteButtonCommand which doesn't exist in Button object. And then, to bind the parameter, you need to point to your view's DataContext.
Change it to:
<ListBox.ItemTemplate>
<DataTemplate>
<Button Margin="5,5,5,5"
Content="{Binding Content}"
Command="{Binding Command}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=Window},Path=DataContext.MyParameter}"
/>
</DataTemplate>
</ListBox.ItemTemplate>
For clarification, I created a property called "MyParameter" in my ViewModel. Also, in your codebehind, change your button creation code to:
Buttons = new ObservableCollection<Button>();
Button btn = new Button();
btn.Content = "Generate Files";
btn.Command = ExecuteButtonCommand;
Buttons.Add(btn);
And your ExecuteButtonCommand to simply:
public ICommand ExecuteButtonCommand
{
get
{
if (_executeButtonCommand == null)
_executeButtonCommand = new RelayCommand(ButtonCommands);
return _executeButtonCommand;
}
}
I wanted to close this out with the final result in case someone else is searching for the same answer.
Mari set me straight which led to this example below as the final result. There is no "Code Behind." Generation of the buttons is done in the view model. After a button is created it is added to the button collection which is the source for the ListBox. I am only including the code specific to the question.
This is how it ended up.
The View
<ListBox ItemsSource="{Binding Buttons, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="AliceBlue"
BorderBrush="Transparent"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
SelectedItem="">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Margin="5,5,5,5"
Content="{Binding Content}"
Command="{Binding Command}"
CommandParameter="{Binding CommandParameter}"
/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The ViewModel - A switch statement is used to determine what button needs to be generated. I gave the button a name because I wanted to be able to find it in the collection and set the Enabled property. But that didn't work and I still haven't found an answer.
public void AddButton(string param)
{
Button btn = new Button();
switch (param)
{
case "Files":
btn.Content = "Do Files";
btn.CommandParameter = "Files";
btn.Name = "Files";
break;
//More items here
}
btn.Command = ExecuteButtonCommand; //The ICommand name. I made this harder than it needed to be!
Buttons.Add(btn);
}
public RelayCommand _executeButtonCommand;
public ICommand ExecuteButtonCommand
{
get
{
if (_executeButtonCommand == null)
_executeButtonCommand = new RelayCommand(param => this.ButtonCommands(param));
return _executeButtonCommand;
}
}
I hope that can help someone.
I noticed a strange bug. When i bind a command to canvas MouseLeftButtonDown event it does not fire. I tried to debug and noticed that it fires, but only during initialization. I guess the crux is in binding. Here's the code:
<ItemsControl Grid.Row="1" ItemsSource="{Binding Polygons}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas>
<i:Interaction.Behaviors>
<behaviours:MouseBehaviour MouseX="{Binding MouseX, Mode=OneWayToSource}" MouseY="{Binding MouseY, Mode=OneWayToSource}" />
</i:Interaction.Behaviors>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<command:EventToCommand Command="{Binding SelectPointCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Canvas>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
/* some data template
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
And command implementation:
public ICommand SelectPointCommand
{
get
{
if (!CanEdit)
return new RelayCommand(e => { });
ClickCounter++;
if (ClickCounter == 3)
{
ClickCounter = 0;
CanEdit = false;
}
return new RelayCommand(
() =>
{
Polygons.Add(new Polygon(ClickedPoints));
ClickedPoints.Clear();
});
}
}
I guessed problem here is in MouseBehaviour but deleting this piece of code also didnt help.
ps : I tried setting canvas Background property and it didnt work.
As well as setting command to this
SelectPointCommand = new RelayCommand(
() =>
{
System.Windows.MessageBox.Show("Test");
},
() => true);
EDIT
I tried to call method like this :
<Canvas Background="Transparent" MouseLeftButtonDown="UIElement_OnMouseLeftButtonDown">
</Canvas>
And the code behind:
private void UIElement_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
((MainViewModel)DataContext).SelectPointCommand.Execute(e);
}
Method UIElement_OnMouseLeftButtonDown isn't invoked anyway;
Changing Canvas to StackPanel had same result.
It is hard to check what is wrong as you don't post all your code. CanEdit property is changed in some other places? What is ClickCounter for?
I think the problem is with getter of SelectPointCommand. It is executed only once, at the moment of creating binding. I would also make use of CanExecute method of ICommand and store returned value of getter in private field. for example:
private ICommand _selectPointCommand;
ICommand SelectPointCommand
{
get
{
Console.WriteLine("This is executed once");
return _selectPointCommand;
}
set
{
if (_selectPointCommand != value)
{
_selectPointCommand = value;
OnPropertyChanged("SelectPointCommand");
}
}
}
In constructor of ViewModel:
SelectPointCommand = new RelayCommand(
(x) =>
{
Console.WriteLine("This is executed every click");
ClickCounter++;
if (ClickCounter == 3)
{
ClickCounter = 0;
CanEdit = false;
}
Polygons.Add(new Polygon(ClickedPoints));
ClickedPoints.Clear();
},
(x) => { return CanEdit; });
Okay guys, i fixed this bug. The problem was here:
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
Setting row's Height to "Auto" meant setting it to zero. So canvas just didn't exist! I left it like this :
<RowDefinition/>
After that everything worked just fine.
I am triggering password change event and command action
xaml
<PasswordBox VmWindow:PasswordHelper.Attach="True" Height="25" Width="180" HorizontalAlignment="Left"
FontFamily="Arial" FontSize="11" BorderBrush="#FF959BA0" TabIndex="2"
VmWindow:PasswordHelper.Password="{Binding Path=Password, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="PasswordChanged">
<i:InvokeCommandAction Command="{Binding ChangePasswordCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</PasswordBox>
Password changed event code:
private static void PasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
SetIsUpdating(passwordBox, true);
SetPassword(passwordBox, passwordBox.Password);
SetIsUpdating(passwordBox, false);
}
Command Action
public ICommand ChangePasswordCommand { get; private set; }
public LoginViewModel ViewModel { get; set; }
private void ExecuteChangePasswordCommand()
{
try
{
if (loginModel != null)
{
loginModel.LoginPassword = Password;
}
}
catch (Exception exception)
{
}
}
Problem:
What is happening intially when application loads i enter char in textbox the "Event fires first and then action which ok normal senario",but when i press second char "my Action fires first then trigger that is the problem"
I don't know weather it is predefined process or am i doing something wrong .Because according to me order should always Event,action not Action,event
Please let me know where i am wrong.
Finally after spending a lot of time i am able to solve the above problem with some additional changes
Add Reference of Microsoft.Expressions.Interactions
Add two refrences:
xmlns:i="clrnamespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:ei=http://schemas.microsoft.com/expression/2010/interactions
Done few changes in password box:
<PasswordBox VmWindow:PasswordHelper.Attach="True" Height="25" Width="180" HorizontalAlignment="Left"
FontFamily="Arial" FontSize="11" BorderBrush="#FF959BA0" TabIndex="2"
VmWindow:PasswordHelper.Password="{Binding Path=Password, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="PasswordChanged">
<ei:CallMethodAction TargetObject="{Binding}" MethodName="changePassword"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</PasswordBox>
Create a Method in Your LoginViewModel
public void changePassword(object sender, RoutedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
loginModel.LoginPassword = passwordBox.Password;
}
Done With changes its clean MVVM with no code behind now