C# EventHandler Beautiful Code (How To?) - wpf

I admit, it is kind of tiny, but I am looking for better ways to do the following code blocks. They should be self explaining...
private void listBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var listBoxItem = sender as ListBoxItem;
if (listBoxItem != null)
{
var clickObject = listBoxItem.DataContext as ClickObject;
if (clickObject != null)
{
clickObject.SingleClick();
}
}
}
Another ugly one:
private void listBox_SelectionChangedA(object sender, SelectionChangedEventArgs e)
{
var lB = sender as ListBox;
if (lB != null)
StatusBoxA.Text = "Elements selected" + lB.SelectedItems.Count;
}
Yeah, I know, its not near-death-urgent. But I do NOT like the (if != null). Any magic ideas to shorten it even more :-)
Btw, I found some nice info about a similar topic: Loops on Null Items
Nice to read...

I love good, clean code but in most cases, clean & elegant doesn't mean short and smart. Code brevity is good for competitions. Changing an "if not null" statement to a foreach might seem way cooler but it's harder for everyone else working in the project to understand what you are trying to accomplish. Believe me, even you won't remember it a few months later :P. Your code is just fine as it is!

private void listBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var listBoxItem = sender as ListBoxItem;
if (listBoxItem == null) return;
var clickObject = listBoxItem.DataContext as ClickObject;
if (clickObject == null) return;
clickObject.SingleClick();
}

One-liner:
private void listBox_SelectionChangedA(object sender, SelectionChangedEventArgs e)
{
As<ListBox>(sender, (lB) => StatusBoxA.Text = "Elements selected" + lB.SelectedItems.Count);
}
or, nested:
private void listBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
As<ListBoxItem>(sender, (listBoxItem) => {
As<ClickObject>(listBoxItem.DataContext,
(clickObject) => clickObject.SingleClick());
};
}
using this static generic method (T is destination type, input is object to cast, code is a delegate (or lambda expression) to execute on success:
static void As<T>(object input, Action<T> code) where T : class
{
T foo = input as T;
if (foo != null)
code(foo);
}

Since you're using known events from the .NET framework (as opposed to a third party) and from the code it looks like you're only using those methods for specific classes (i.e. ListBoxItems and ListBoxes), there are a few things you know to be true:
sender will never be null
sender will always be a ListBoxItem, or ListBox, respectively
So why use the as operator? Just cast!
Then the first snippet becomes
private void listBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
var listBoxItem = (ListBoxItem)sender;
var clickObject = (ClickObject)listBoxItem.DataContext;
clickObject.SingleClick();
}
Note this isn't true in the general case (you wouldn't do this if you were handling all PreviewMouseDown events in that one handler for all Control types), but for event handling code like this, especially in UI code, you can be as certain as you can be of anything, that sender will not be null and sender will be of the type you expect.

Maybe I am just being pedantic but why do you need to cast the sender if you are using the event within its host containers code.
Regardless of who made the change to a list, couldn't you just give your listbox a name and use that.
<ListBox x:Name="listbox1" />
private void listBox_SelectionChangedA(object sender, SelectionChangedEventArgs e)
{
StatusBoxA.Text = "Elements selected" + listbox1.SelectedItems.Count;
}
Or you could even achieve some of this using binding with no code behind.

This is supposed to be the same as the first one, reformatted a little:
private void listBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
ClickObject clickObject;
if (
((sender as ListBoxItem) != null) &&
((clickObject = ((ListBoxItem)sender).DataContext as ClickObject) != null)
)
{
clickObject.SingleClick();
}
}

You can add extensions methods to Form elements, which can then trigger the events:
public static void OnSelectionChanged(this ListBox b, Action<ListBox> a)
{
b.SelectedIndexChanged += (s,e) =>
{
if (s is ListBox)
a(s as ListBox);
};
}

Using the same idea as Utaal's solution, but as an extension method...
public static void As<TSource>(this object item, Action<TSource> action) where TSource : class
{
var cast = item as TSource;
if (cast != null)
action(cast);
}
private void listBoxItem_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
sender.As<ListBoxItem>(listBoxItem =>
listBoxItem.DataContext.As<ClickObject>(clickObject =>
clickObject.SingleClick()));
}

Related

Why does WPFMediaKit VideoCaptureElement created by code land ever in MediaFailed?

I add a VideoCaptureElement to a window in runtime but when I run this code it fires MediaFailed. But if I add the same element in XAML then it works fine, I can see the video from the laptop camera.
Am I doing anything wrong? Please help!
public partial class MainWindow : Window
{
WPFMediaKit.DirectShow.Controls.VideoCaptureElement VCE;
public MainWindow()
{
InitializeComponent();
VCE = new WPFMediaKit.DirectShow.Controls.VideoCaptureElement();
Content = VCE;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
VCE.MediaOpened += VCE_MediaOpened;
VCE.MediaFailed += VCE_MediaFailed;
VCE.VideoCaptureDevice = WPFMediaKit.DirectShow.Controls.MultimediaUtil.VideoInputDevices[0]; // This is my laptop webcam
}
void VCE_MediaOpened(Object sender, RoutedEventArgs e) { ... }
void VCE_MediaFailed(object sender, WPFMediaKit.DirectShow.MediaPlayers.MediaFailedEventArgs e) { ... }
}
I had a similar problem with a MediaUriElement working in XAML but not working when instantiated in code-behind.
The solution for me was to Init the control:
VCE.BeginInit();
VCE.EndInit();
This would fit between instantiating (VCE = new...) and assigning (Content = VCE). I haven't tested your particular scenario, but it sounds like the same cause - there must be some extra work done in Init that happens automatically when using XAML.

Interact with the Ok/Accept Or Cancel Button of a RepositoryItemTimeSpanEdit?

as seen in this post, I need to interact with the button, I mean, save the value of the repository when the user press the OK button, any suggest?
You need to find your TimeSpanEdit control inside of the popup form. You can iterate through popupForm.Controls collection to find out the control with TimeSpanEdit type. Here is example of how to do it. After that you can use TimeSpanEdit.TimeSpan property to get the value of TimeSpanEdit control.
private void OkButton_Click(object sender, EventArgs e)
{
var popupForm = (TimeSpanEditDropDownForm)OwnedForms.FirstOrDefault(item => item is TimeSpanEditDropDownForm);
if (popupForm == null)
return;
var timeSpanEdit = GetAll(this, typeof(TimeSpanEdit)).FirstOrDefault();
if (timeSpanEdit == null)
return;
MessageBox.Show(timeSpanEdit.TimeSpan.ToString());
}
public IEnumerable<Control> GetAll(Control control,Type type)
{
var controls = control.Controls.Cast<Control>();
return controls.SelectMany(ctrl => GetAll(ctrl,type))
.Concat(controls)
.Where(c => c.GetType() == type);
}
I think you can use object sender. sender will contains probably TimeSpanEditDropDownForm and there you should get actual value of this form. :)
I presume this code is called from controller is it?
if it is true than you have View.CurrentObject and you must know which property uses this TimeSpanEditDropDownForm so you could do something like this.
private void OkButton_Click(object sender, EventArgs e)
{
MyClass myClass = View.CUrrentObject as MyClass;
TimeSpanEditDropDownForm timeSpanForm = sender as TimeSpanEditDropDownForm;
myClass.CurrentTime = timeSpanForm.CurrentTime;
myClass.Session.CommitChanges();
MessageBox.Show("Ok");
}
I dont know what is name of right attribute wich store TimeSpan inside TimeSpanEditDropDownForm thats thing you must find out but I think it could helps :)

Got null reference exception in Loaded event, where the count of SelectedItems in a child datagrid is accessed

Here is the top of the exception stack:
System.NullReferenceException: Object reference not set to an instance of an object.
at xx.TblQcLotListSelectionChanged(Object sender, SelectionChangedEventArgs e) in ...\InternalEventHandlerQCPage.cs:line 441
at xx.QCLotListPageLeftLoaded(Object sender, RoutedEventArgs e) in ...\InternalEventHandlerQCPage.cs:line 435
And this is the mentioned part of the InternalEventHandlerQCPage.cs:
private void QCLotListPageLeftLoaded(object sender, RoutedEventArgs e)
{
this.QCPage.QCShowRangesTblHitTest = true;
this.QCPage.QCShowRangesTblEnabled = true;
this.QCPage.LOTListRightTxtNameEnabled = false;
this.QCPage.LOTListFieldEnabled = false;
this.QCPage.LOTListNumberDateEnabled = false;
TblQcLotListSelectionChanged(null, null); //line 435
}
private void TblQcLotListSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (this._QCLotListPageLeftXAML.tblLotList.SelectedItems.Count == 0) //line 441
{
this.QCPage.LOTListRightBtnUpEnabled = false;
this.QCPage.LOTListRightBtnDownEnabled = false;
this.QCPage.QCShowRangesBtnUpEnabled = false;
this.QCPage.QCShowRangesBtnDownEnabled = false;
...
"this" is a UserControl.
_QCLotListPageLeftXAML is also a UserControl used on a page inside a TabControl
tblLotList is a WPF DataGrid
QCPage is a dependency object containing only dependency properties for bindings
We got this exception when the datagrid had 300 items, which is the allowed maximum that we set. The datagrid has 4 columns, so it's not that heavy... Also the software has been running for a while before, so this hasn't occured on the sw start.
Do you have any idea what can go wrong here? SelectedItems can be null for a DataGrid?
Try, if the error occurs still, when you do this:
if (this._QCLotListPageLeftXAML.tblLotList.SelectedItems != null &&
this._QCLotListPageLeftXAML.tblLotList.SelectedItems.Count == 0)
{
}
Problem solved. It was another case when a released, compiled application complained for an other line in the stack than the line which actually caused the exception.

WPF ListView - How to copy individual cells

I have code where I can take a look at the SelectedItem and then output ToString() to get the record into the clipboard.
How can I detect what cell the user is right clicking on in order to copy just that cell in the SelectedItem?
For example, if I have Borrower Information and the user right-clicks on last name, I would like to give the ability to just copy last name to clipboard.
Thank you!
UPDATE:
Here is the code that I used as suggested by Josh, it worked great:
private void BorrowerInfoCopyClicked(object sender, RoutedEventArgs e)
{
BorrowerViewModel vm = this.DataContext as BorrowerViewModel;
if (vm != null)
{
Clipboard.SetData(DataFormats.Text, vm.CurrentTextBlockText);
}
}
private void AddressCopyClicked(object sender, RoutedEventArgs e)
{
BorrowerViewModel vm = this.DataContext as BorrowerViewModel;
if (vm != null)
{
Clipboard.SetData(DataFormats.Text, vm.CurrentTextBlockText);
}
}
private void lstViews_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
BorrowerViewModel vm = this.DataContext as BorrowerViewModel;
if (vm != null)
{
if (e.OriginalSource is TextBlock)
{
TextBlock txtBlock = e.OriginalSource as TextBlock;
vm.CurrentTextBlockText = txtBlock.Text;
}
}
}
I've done this by handling the PreviewMouseRightButtonDown event on the ListView and checking if e.OriginalSource is a TextBlock. If so, copy the txtBlk.Text to the clipboard. This code could either be in the code-behind of the View that contains the ListView, or as a behavior you attach to the ListView. If you need to use a context menu to perform the Copy operation, have a TextBlock field that you use to store a reference to the TextBlock, and in your method that responds to a MenuItem's click (or Command execution) reference the TextBlock there instead.

Intercepting the value change of SetChildIndex

In a .NET CF-form i have multiple panels. I want to have a property that should always be informed about if a panel is in the front.
Can this be done using the GetChildIndex() method?
If yes, how do i intercept the change to SetChildIndex()?
Thanks in advance
For everybody who is interested for future use:
simply add a new event handler for the Paint event of each panel, for example:
panel1.Paint += new PaintEventHandler(panel1_Paint);
panel2.Paint += new PaintEventHandler(panel2_Paint);
and in each of the event handlers just call a Method which retrieves the state of all the panels like so:
void panel2_Paint(object sender, PaintEventArgs e)
{
GetPanelStates();
}
void panel1_Paint(object sender, PaintEventArgs e)
{
GetPanelStates();
}
void GetPanelStates()
{
Panel2IsInFront = panel2.Parent.Controls.GetChildIndex(panel2) == 0;
Panel1IsInFront = panel1.Parent.Controls.GetChildIndex(panel1) == 0;
}

Resources