Listen for generic events - c#

I have a function I would like to run on after update of a lot of different text boxes, is it possible to listen for a generic after update event rather than the specific events?
So rather than 100 individual calls to the function, just one listener?
Edit: It would appear we are using a combination of MVVM and traditional code behind.
Here is one of the textboxes:
<TextBox Text="{Binding APhaseFrom}" x:Name="txtFromWhereA" TabIndex="26" HorizontalContentAlignment="Center" HorizontalAlignment="Left" Height="48" TextWrapping="NoWrap" VerticalAlignment="Top" Width="261" FontSize="26" FontWeight="Bold" BorderBrush="Black" BorderThickness="1" Margin="289,656,0,0" GotMouseCapture="txtFromWhereA_GotMouseCapture" GotFocus="txtFromWhereA_GotFocus" Grid.Row="3" />
The code from the view Model:
public string APhaseFrom
{
get { return new string((char[])_f.Rows[1].GetValue("Alpha09")); }
set
{
if (value.Length <= 35)
{
_f.Rows[1].SetValue("Alpha09", value);
}
else
{
MessageBox.Show("Error: String length Longer than 35 Characters.");
}
}
}
We also are using some commands for other processes:
public ICommand Updatesql
{
get;
internal set;
}
private void CreateUpdatesql()
{
Updatesql = new RelayCommand(UpdatesqlExecute);
}
private void UpdatesqlExecute()
{
_f.Update();
}
Should I be using commands or just link the events to functions in the viewmodel?

Since you are using WPF, and if I understand your problem correctly, then the RoutedEvents that WPF uses may help you here. Essentially, events like the LostFocus event of a TextBox will bubble up your UI hierarchy and can be handled by a common parent control. Consider this snippet of XAML and codebehind:
<StackPanel TextBox.LostFocus="TextBoxLostFocus">
<TextBox></TextBox>
<TextBox></TextBox>
<TextBox></TextBox>
</StackPanel>
Codebehind:
private void TextBoxLostFocus(object sender, RoutedEventArgs e)
{
MessageBox.Show("Lost Focus!");
}
You will find that the event handler is called for any of the three textboxes when focus is lost. The sender parameter or e.Source can be used to find the textbox that fired the event.
This pattern holds true for any RoutedEvent, so things like Button.Click or TextBox.TextChanged and many more can be caught in this manner.

Really and truthfully you should be using a single design pattern... ie MVVM when writing WPF applications, each textbox would be bound to a property which implements the INotifyPropertyChange interface.
In the setter of each property you would essentially update the value, fire a property changed event and then either make a call to your method or simply add an event handler on the view model for the PropertyChanged event.
Also... MessageBox.Show is a bad idea in your view models, its hard to unit test it.

Update
I removed my previous ideas because I now understand more clearly what you are looking for.
But you definitely need to use the LostFocus event.
<TextBox Text="{Binding APhaseFrom}" x:Name="txtFromWhereA" LostFocus="OnLostFocus" />

Related

Why are my custom controls not always receiving MouseEnter events?

Alright, I'm fairly new to WPF and I ran into a very strange problem. The relevant section of my XAML defines a Border around a ScrollViewer around a StackPanel which is populated using an ItemsControl that is then databound to a CollectionViewSource which in turn wraps a standard ObservableCollection. The ItemsControl defines a DataTemplate that contains only one tag: a custom control I've made called a StackElement. I'm handling three events from this control — MouseEnter, MouseLeave, and PreviewMouseLeftButtonUp. These events can fire, but do so unreliably.
For example, after some new StackElements are added, the MouseEnter event generally doesn't fire on the first StackElement until I've moused over a few others. Once a MouseOver manages to fire once, it continues to fire correctly on that StackElement from there on out.
However, the first time mousing over a StackElement doesn't always fail. If I approach the StackElements from beneath and try the last one first, it will always fire. When I do this, sometimes the first one will work, but the second one won't fire. Once, both of them did manage to operate correctly, but it happens infrequently.
I'm not multithreading anything, none of my parent controls handle events of their own, all event handlers consist only of a WriteLine() statement for debugging purposes, and the StackElement code-behind isn't handling any events either.
I've tried decoupling the ItemsControl from the CollectionViewSource in favor of binding it directly to the ObservableCollection, which did nothing other than (as I expected) bypass the sorting functionality I added to the ViewSource. I tried handling the events in the StackElement class itself, in addition to making them be tied to other controls contained within StackElement. I tried using DataTriggers, which if I remember worked as expected, but I need to include more advanced logic such as multiselection and the inability to lightly highlight an already-selected StackElement.
For context, I'm intending to use these events to lightly highlight StackElements when the user drags the mouse over them and to strongly highlight them when the mouse is pressed — basically, I need something that looks and feels like Windows File Explorer. From what I've seen this can't be accomplished in an elegant fashion with DataTriggers alone.
Here's my event handlers (in MainWindow.xaml):
private void StackElement_OnPreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
Console.WriteLine("OnPreviewMouseLeftButtonUp fired for a StackElement.");
}
private void StackElement_OnMouseEnter(object sender, MouseEventArgs e)
{
Console.WriteLine("OnMouseEnter fired for a StackElement.");
}
private void StackElement_OnMouseLeave(object sender, MouseEventArgs e)
{
Console.WriteLine("OnMouseLeave fired for a StackElement.");
}
Here's how I'm adding to the bound collection (for testing, which is why it's hooked up to a random button):
private void Btn_File_PreviewMouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
InitiativeStackElement t = new InitiativeStackElement(new Entity("TestName", 10, 11, 12, 13, null)); //InitiativeStackElement implements INotifyPropertyChanged so the databindings work
_entityProvider.Elements.Add(t); //_entityProvider is just a reference to a XAML-defined resource class, which is loaded up in the constructor so I don't have to call TryGetResource() whenever I want to use it. it's currently used for testing purposes only
}
Finally, here's the portion of my XAML containing the StackElements:
<Border Grid.Row="1"
Margin="0,1,0,0"
Style="{StaticResource StandardBorder}">
<ScrollViewer Name="Scv_InitiativeStack">
<StackPanel Name="Stp_InitiativeStack">
<ItemsControl Name="Its_InitiativeStack" ItemsSource="{Binding Source={StaticResource SortedInitiativeStack}}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<con:StackElement Element="{Binding}" PreviewMouseLeftButtonUp="StackElement_OnPreviewMouseLeftButtonUp" MouseEnter="StackElement_OnMouseEnter" MouseLeave="StackElement_OnMouseLeave"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</Border>
The StackElement class just defines a single DependencyProperty of type InitiativeStackElement. The properties of this object are bound to a few controls within the StackElement, which always displays correctly. It's the behavior of the events that have me confused.
As described, I'm expecting the MouseEnter event to fire whenever the mouse is dragged onto the StackElement. However, it's only firing after I fulfill seemingly random conditions that shouldn't affect it's functionality, like mousing over another StackElement first. There are no error messages.
Alright, I was able to get the functionality I wanted using ListBox:
<Window.Resources>
<DataTemplate x:Key="InitiativeStackTemplate">
<con:StackElement Element="{Binding}"/>
</DataTemplate>
</Window.Resources>
<Border Margin="0,1,0,0"
Grid.Row="1"
Style="{StaticResource StandardBorder}">
<ScrollViewer Name="Scv_InitiativeStack">
<ListBox Name="Lbx_InitiativeStack"
SelectionMode="Extended"
ItemsSource="{Binding Source={StaticResource SortedInitiativeStack}}"
ItemTemplate="{StaticResource InitiativeStackTemplate}"
HorizontalContentAlignment="Stretch"/>
</ScrollViewer>
</Border>
Everything works as expected.

How to build my own close button?

I have a wpf application with a MVVM. I am trying here to build my own close button. Based on this answer Creating a custom Close Button in WPF I added a button event handler in the View(xaml.cs) code. However, it is not recognizing the Close(); call (doesn't exist in the context - Can't resolve symbol).
Also I tried the other answer and added Command and CommandParameter into my button's xaml. But the function behind is not getting hits. In How to bind Close command to a button using the RelayCommand also my wpf is not recognizing RelayCommand. Then How can I use the RelayCommand in wpf said that I have to write it myself(really?). I remember there was a simple way similar to just set an event handler for the button and call Close();. But, how can I do that or why it is not working for me?
View code:
private void closeButton_Click(object sender, RoutedEventArgs e)
{
// I want to call close the whole app on button click
//Close(); is not recognized
}
private void performMainCloseButtonCommand(object Parameter)
{
// This doesn't get hits on button click
Window objWindow = Parameter as Window;
objWindow.Close();
}
Button XAML:
<Button x:Name="closeButton" RenderTransformOrigin="0.5,0.5" Padding="0" Margin="701,0,0,0" BorderThickness="0" Click="closeButton_Click" Command="{Binding MainCloseButtonCommand}" CommandParameter="{Binding ElementName = mainWindow}" Height="45" Width="45" >
<StackPanel Height="45" Width="45">
<Image x:Name="closeButtonImage" Margin="0" Source="/ProjectName;component/Resources/x.fw.png" Height="33"/>
<TextBlock Text="Close" Width="36" Padding="6,0,0,0" HorizontalAlignment="Center" Height="13" FontSize="10"/>
</StackPanel>
</Button>
Close isn't recognized in your event handler because there is probably no method called Close in your current class. If you want to call main window's close method you can use:
private void closeButton_Click(object sender, RoutedEventArgs e)
{
Application.Current.MainWindow.Close();
}
Above is not a good way to do this and does not align with MVVM pattern. Which relates to your second question. Without seeing remaining part of your code, its hard to say why command binding isn't working. My guess you haven't wired up the commands properly for it to fire. You will need to ensure that you have created your RelayCommand instance and your command properties are correctly set.

Blend/XAML: bind ItemsSource via button

I am creating a Windows 8.1 app and have the following code:
<ListView x:Name="listView" Height="505" Canvas.Left="35" Canvas.Top="75" Width="300" ItemTemplate="{StaticResource GroupTemplate}" ItemsSource="{Binding Groups, Mode=TwoWay, UpdateSourceTrigger=Explicit}" HorizontalAlignment="Left" VerticalAlignment="Top" UseLayoutRounding="False" ScrollViewer.VerticalScrollBarVisibility="Disabled" SelectionMode="None" />
<Button Height="40" Width="260" HorizontalAlignment="Left" VerticalAlignment="Top" Canvas.Left="40" Canvas.Top="585" BorderThickness="0" Content="Overige tonen" Background="#FF9B9B9B" Style="{StaticResource ButtonStyle1}" Click="show_items2" />
When the user clicks the show_items2 button, the ItemsSource Groups should be replaced by another ItemsSource. I use sampledata 'Groups' from Blend for Visual Studio 2013. I have the pretty obvious code for the button, shown below:
private void show_Medicatie2(object sender, RoutedEventArgs e)
{
// replace itemsSource with another
}
I've tried many tutorials but nothing seams to work. Do I miss something? Help is really appreciated. Thank you!
You need to RaisePropertyChanged("Groups") when you change Groups
The class which you are binding to (in your case, the owner of Groups) is required to be an INotifyPropertyChanged so that you can explicitly tell the WPF that the binding needs refreshing. This is a requirement if you are not using DependencyProperties.
Add this to your class definition:
class X : System.ComponentModel.INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
public IEnumerable<object> Groups //Replace 'object' with the type your are using...
{
get{ return m_Groups; }
set
{
m_Groups = value;
//Raise the event to notify that the property has actually got a new value
var handler = PropertyChanged;
if(handler != null)
PropertyChanged(this, new PropertyChangedEventArgs("Groups"));
}
} IEnumerable<object> m_Groups = new List<object>();
}
Why does this work?
WPF knows about INotifyPropertyChanged and when you use an object as a Binding source, WPF checks if it implements the interface. If it does, WPF subscribes to the event. When the event is triggered, the property name is checked and if it matches the last property in the binding path, the binding is updated.
You can also replace IEnumerable<T> with ObservableCollection<T> but things start getting tricky soon afterwards. I recommended reading up about ObservableCollection<T>!

Raise event if any control has been changed WPF

I want make validation in my WPF application. And the approach I want to use is I want to take custom action if any value of my controls has been changed.
How I can do that suppose I have control like this
<TextBox x:Name="TextBox1" Text="{Binding Path=Box1,UpdateSourceTrigger=PropertyChanged}" />
<TextBox x:Name="TextBox2" Text="{Binding Path=Box2,UpdateSourceTrigger=PropertyChanged}" />
And If user change the value one of that control I want to create custom action for example (For my thinking )
private void Window_Controls_Property_Changed(object sender,RoutedEventArgs e){
if(((Control)sender)=="TextBox1")
MessageBox.Show("Show message here of validation some control","Attention",MessageBoxButton.OK);
}
I'm still confusing combine the INotifyPropertyChanged or ValidationRule with displaying error what I want with MessageBox. I'm still newbie using this kind of features.
I have read many articles about INotifyPropertyChanged or ValidationRule but I'm still don't know how to get the custom action like I said before. And the custom validation may use another textbpx value to validate with the other textbox.
Any helps?..
You can register the handler on the Container that hosts your TextBox's. Like this:
<StackPanel TextBox.LostFocus="TextBox_OnLostFocus">
<TextBox x:Name="TextBox1" Text="{Binding Path=Box1,UpdateSourceTrigger=PropertyChanged}" />
<TextBox x:Name="TextBox2" Text="{Binding Path=Box2,UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
This way the same handler will be used for all TextBox's and you can query e.Source to find out which TextBox triggered the event.
for Validation in mvvm you can take IDataErrorInfo. there are examples all over the net.
and if you really whant to do some special action when a property in your viewmodel change, you can simply subscribe to your own viewmodel PropertyChanged event and handle your stuff
You may want to read this block post about BindingGroups and validation (and this related blog post). They show you how you can validate multiple controls at once using custom validation rules.
<TextBox HorizontalAlignment="Left" LostFocus="OnLostFocus"/>
Here's an easy way. Validate using the LostFocus Event in WPF
private void TextBox_LostFocus_1(object sender, RoutedEventArgs e) {
var thisTextBox = (TextBox)sender;
if (thisTextBox.Text == "") {
MessageBox.Show("Validate!");
}
}

WPF Check box: Check changed handling

In WPF data binding, I can bind the IsChecked property to some data, e.g. user setting, but I need to handle "CheckChanged" event, I know I can seperately handle Checked, Unchecked event, but is there any way to get notified when this value is changed?
<CheckBox Content="Case Sensitive" IsChecked="{Binding bSearchCaseSensitive,
Source={x:Static Properties:Settings.Default}}" />
Note: I don't care if it is checked or unchecked. I just want to be notified when it is changed.
That you can handle the checked and unchecked events seperately doesn't mean you have to. If you don't want to follow the MVVM pattern you can simply attach the same handler to both events and you have your change signal:
<CheckBox Checked="CheckBoxChanged" Unchecked="CheckBoxChanged"/>
and in Code-behind;
private void CheckBoxChanged(object sender, RoutedEventArgs e)
{
MessageBox.Show("Eureka, it changed!");
}
Please note that WPF strongly encourages the MVVM pattern utilizing INotifyPropertyChanged and/or DependencyProperties for a reason. This is something that works, not something I would like to encourage as good programming habit.
As a checkbox click = a checkbox change the following will also work:
<CheckBox Click="CheckBox_Click" />
private void CheckBox_Click(object sender, RoutedEventArgs e)
{
// ... do some stuff
}
It has the additional advantage of working when IsThreeState="True" whereas just handling Checked and Unchecked does not.
Im putting this in an answer because it's too long for a comment:
If you need the VM to be aware when the CheckBox is changed, you should really bind the CheckBox to the VM, and not a static value:
public class ViewModel
{
private bool _caseSensitive;
public bool CaseSensitive
{
get { return _caseSensitive; }
set
{
_caseSensitive = value;
NotifyPropertyChange(() => CaseSensitive);
Settings.Default.bSearchCaseSensitive = value;
}
}
}
XAML:
<CheckBox Content="Case Sensitive" IsChecked="{Binding CaseSensitive}"/>
I know this is an old question, but how about just binding to Command if using MVVM?
ex:
<CheckBox Content="Case Sensitive" Command="{Binding bSearchCaseSensitive}"/>
For me it triggers on both Check and Uncheck.
A simple and proper way I've found to Handle Checked/Unchecked events using MVVM pattern is the Following, with Caliburn.Micro :
<CheckBox IsChecked="{Binding IsCheckedBooleanProperty}" Content="{DynamicResource DisplayContent}" cal:Message.Attach="[Event Checked] = [Action CheckBoxClicked()]; [Event Unchecked] = [Action CheckBoxClicked()]" />
And implement a Method CheckBoxClicked() in the ViewModel, to do stuff you want.
What about the Checked event? Combine that with AttachedCommandBehaviors or something similar, and a DelegateCommand to get a function fired in your viewmodel everytime that event is called.

Categories

Resources