Can if statements be combined with a variable assignment using pattern matching? - c#

Example of the code that I am talking about:
if (sender is Panel p)
{
if (p.Enabled == false)
{
AMButton.Checked = false;
PMButton.Checked = false;
currentSelectedTime = null;
}
}
Is it possible to "do something more" with the variable p once its been cast in the first if statement thus combining the two statements?

Something such as if(sender is Panel p.Enabled). Combining the two if
statements into one line like that. Does that make sense?
if(sender is Panel p && p.Enabled)
or to get real ugly
if(sender is Panel p && (p.Enabled = somethingElse) == ((someInt = anotherInt) == 5))

The accepted answer of
if (sender is Panel p && p.Enabled)
is correct and works right now.
In C# 8, pattern matching will (probably) be extended to allow property matching:
if (sender is Panel { Enabled: true } p)
This looks a little alien right now, but it's likely to be more and more idiomatic, particularly when matching multiple properties. The recursive patterns can be used to introduce more pattern variables too, and you don't need a pattern variable for the "outer" pattern. For example, suppose we only needed the Tag property from the panel, we could use:
if (sender is Panel { Enabled: true, Tag: var tag })
{
// Use tag in here
}

Related

How to Group and Sort Objects in ObjectListView?

I am trying to group my list of objects in an ObjectListView.
The ObjectListView should group the objects based on the first column but then have that same column sorted based on a custom sort.
How do I do that? I have read through the documentation for ObjectListView:
http://objectlistview.sourceforge.net/cs/gettingStarted.html#gettingstarted
So far, I have implemented my custom sort but I am not sure how to trigger the grouping? Remember that I am trying to group on the first column but then apply a custom sort.
My custom sort relis on the BeforeSorting event:
// after initializing components
olv.BeforeSorting += olv_BeforeSorting;
Then...
private void olv_BeforeSorting(object sender,BrightIdeasSoftware.BeforeSortingEventArgs e)
{
olvDataSource.Sort((x, y) => x.Group.ID.CompareTo(y.Group.ID));
e.Handled = true;
}
The ObjectListView displays my ordered object list but it is not grouped together. Each object displays on its own row without a group heading.
How do I group my objects after sorting them?
You can force the grouping column as follows:
olv.ShowGroups = true;
olv.AlwaysGroupByColumn = olvColumn1;
If you want to show one value in the column and group by a different one you can use GroupByKeyGetter
olvColumn1.GroupKeyGetter = GroupKeyGetter;
Delegate would be something like:
private object GroupKeyGetter(object rowObject)
{
var o = rowObject as MyClass;
if(o == null)
return "unknown";
return o.ID;
}
Some stuff doesn't take affect till you call
olv.RebuildColumns();
Always Sort By (Arbitrary Function)
If you want to force sorting on some custom logic you can use ListViewItemSorter in the BeforeSorting event. This is similar to registering a CustomSorter (but that doesn't seem to work when ShowGroups is true).
olv.BeforeSorting += olv_BeforeSorting;
Then
private void olv_BeforeSorting(object sender, BrightIdeasSoftware.BeforeSortingEventArgs e)
{
//example sort based on the last letter of the object name
var s = new OLVColumn();
s.AspectGetter = (o) => ((MyClass)o).Name.Reverse().First();
this.olv.ListViewItemSorter = new ColumnComparer(
s, SortOrder.Descending, e.ColumnToSort, e.SortOrder);
e.Handled = true;
}
I am sharing this for anyone that might come across here looking for a way to apply a custom sort on groups within an ObjectListView.
There might be better ways to do this, but this way worked for me.
colFirst.GroupFormatter = (BrightIdeasSoftware.OLVGroup group, BrightIdeasSoftware.GroupingParameters parms) =>
{
ObjectA a = (OjectA)group.Key;
/* Add any processing code that you need */
group.Task = " . . . ";
group.Header = "Special Name: " + a.Name;
group.Subtitle = $("Object A: {a.Index}, Total Water Consumption: {a.WaterConsumption}");
// This is what is going to be used as a comparable in the GroupComparer below
group.Id = a.ID;
// This will create the iComparer that is needed to create the custom sorting of the groups
parms.GroupComparer = Comparer<BrightIdeasSoftware.OLVGroup>.Create((x, y) => (x.GroupId.CompareTo(y.GroupId)));
};
The OLVColumn.GroupFormatter is lightly explained here:
http://objectlistview.sourceforge.net/cs/recipes.html#how-do-i-put-an-image-next-to-a-group-heading
This works and is basically what is described in the cookbook here http://objectlistview.sourceforge.net/cs/recipes.html?highlight=sort#how-can-i-change-the-ordering-of-groups-or-rows-within-a-group
First subscribe to the olv BeforeCreatingGroups event.
Then create a custom sort comparator in the event handler. In this case for a group matching "Turtles" it will push to the end of the sort, but you can obviously have however much convoluted logic you want in there.
private void Olv_BeforeCreatingGroups(object sender, CreateGroupsEventArgs e)
{
e.Parameters.GroupComparer = Comparer<BrightIdeasSoftware.OLVGroup>.Create(
(x, y) => (
x.Header == "Turtles" ? 1
: x.GroupId.CompareTo(y.GroupId)
)
);
}
This is what I used initially since it's what was in the cookbook. But I ended up switching to something more like Marwan's answer because that one creates a space to reconfigure the group headers themselves.

Find multiple controls with by partially matching their name

I currently have 100+ labels, with names like:
labelNumber1
labelNumber2
labelNumber3
labelNumber4
....
labelLetter1
labelLetter2
labelLetter3
labelLetter4
....
How would I find all the labels that have "Number" in the controls name?
Instead of having to type out labelNumber1.text = "hello", etc.
I have tried regex and foreach with wild cards but did not succeed.
I have looked on msdn.microsoft.com about using regex with a control.
You can loop through the Controls collection of the form and just check the name of each control that it contains something like 'Label'. or you could check that the control is a typeof TextBox, Label, etc.
E.g.
foreach (Control control in form.Controls)
{
if (control.Name.ToUpper().Contains("[Your control search string here]"))
{
// Do something here.
}
if (control is TextBox) {
// Do something here.
}
}
you can filter the list of controls to only return the labels. You would also want to make sure the name is greater than 11 chars.
List<Label> allNumberLabels = new List<Label>();
foreach (Label t in this.Controls.OfType<Label>())
{
if (t.Name.Length > 11)
{
if (t.Name.Substring(5, 6).Equals("Number"))
{
allNumberLabels.Add(t);
}
}
}
I know this is an old question, but I am here now, and:
The question asks about searching for multiple controls. This solution actually applies to any type of control.
OP was conflicted between using "Contains" or regex. I vote for regex! string.Contains is a bad idea for this kind of filter, since "CoolButton" has a "Button" in it too"
Anyway, here is the code:
public List<TControlType> FindByPattern<TControlType>(string regexPattern)
where TControlType:Control
{
return Controls.OfType<TControlType>()
.Where(control => Regex.IsMatch(control.Name, regexPattern))
.ToList();
}
Usage:
//some regex samples you can test out
var startsWithLabel = $"^Label"; //Matches like .StartsWith()
var containsLabel = "Label"; //Matches like .Contains()
var startsWithLabelEndsWithNumber = "^Label.*\\d+&"; //matches Label8sdf12 and Label8
var containsLabelEndsWithNumber = "Label.*\\d+&"; //matches MyLabelRocks83475, MyLabel8Rocks54389, MyLabel8, Label8
var hasAnyNumber= "^CoolLabel\\d+$"; //matches CoolLabel437627
var labels = FindByPattern<Label>("^MyCoolLabel.*\\d+&");
var buttons = FindByPattern<Button("^AveragelyCoolButton.*\\d+&");

How to remove last children from stack panel in WPF?

I am adding children to my stackpanel dynamically. What I need is, I want to remove the last children for certain scenario. Is there any option to get last children?
Here is my code:
var row = new somecontrol();
stackpanel.Children.Add(row);
Is there any possible way to remove children.lastOrDefault()?
stackpanel.Children.Last();
Any help would be appreciated. Thanks in advance.
How about:
if(stackpanel.Children.Count != 0)
stackpanel.Children.RemoveAt(stackpanel.Children.Count - 1);
...or if you want to use Linq, just use the OfType<> ExtensionMethod. Then you can do whatever with Linq you wish, like LastOrDefault:
var child = stackpanel.Children.OfType<UIElement>().LastOrDefault();
if(child != null)
stackpanel.Children.Remove(child);
But, the first is probably fastest.
Or you can make your own Extension method, if you want:
class PanelExtensions
{
public static void RemoveLast(this Panel panel)
{
if(panel.Children.Count != 0)
panel.Children.RemoveAt(panel.Children.Count - 1);
}
}
Use like this
stackpanel.Children.RemoveLast();
But Like Xeun mentions an MVVM solution with Bindings would be preferable.

Excluding the GroupBoxes that are inside another GroupBox

Let's say I have 7 group boxes but some of them also have group box inside them and some do not.
now if I want to iterate through those 7 group boxes and apply something to them, is there a way that I can exclude those Child group boxes from this loop?
though i question the choice of implementation (can you use polymorphism instead? what exactly are you trying to do?), there is a Parent property, e.g.
void soSomething(Control ctrl)
{
if (ctrl is GroupBox && (ctrl.Parent is null || !(ctrl.Parent is GroupBox)))
{
//do something here
}
foreach(Control child in ctrl.Controls)
{
doSomething(child);
}
}
Mark them with the tag property, or something.

Best way to check if a drop down list contains a value?

When the user navigates to a new page, this ddl's selected index is determined by a cookie, but if the ddl doesn't contain that cookie's value, then I'd like it to be set the 0. What method would I use for the ddl? Is a loop the best way, or is there a simply if statement I can perform?
This is what I've attempted, but it doesn't return a bool.
if ( !ddlCustomerNumber.Items.FindByText( GetCustomerNumberCookie().ToString() ) )
ddlCustomerNumber.SelectedIndex = 0;
There are two methods that come to mind:
You could use Contains like so:
if (ddlCustomerNumber.Items.Contains(new
ListItem(GetCustomerNumberCookie().ToString())))
{
// ... code here
}
or modifying your current strategy:
if (ddlCustomerNumber.Items.FindByText(
GetCustomerNumberCookie().ToString()) != null)
{
// ... code here
}
EDIT: There's also a DropDownList.Items.FindByValue that works the same way as FindByText, except it searches based on values instead.
That will return an item. Simply change to:
if (ddlCustomerNumber.Items.FindByText( GetCustomerNumberCookie().ToString()) != null)
ddlCustomerNumber.SelectedIndex = 0;
If 0 is your default value, you can just use a simple assignment:
ddlCustomerNumber.SelectedValue = GetCustomerNumberCookie().ToString();
This automatically selects the proper list item, if the DDL contains the value of the cookie. If it doesn't contain it, this call won't change the selection, so it stays at the default selection. If the latter one is the same as value 0, then it's the perfect solution for you.
I use this mechanism quite a lot and find it very handy.
What about this:
ListItem match = ddlCustomerNumber.Items.FindByText(
GetCustomerNumberCookie().ToString());
if (match == null)
ddlCustomerNumber.SelectedIndex = 0;
//else
// match.Selected = true; // you'll probably select that cookie value
On C# this works:
if (DDLAlmacen.Items.Count > 0)
{
if (DDLAlmacen.Items.FindByValue("AlmacenDefectoAndes").Value == "AlmacenDefectoAndes")
{
DDLAlmacen.SelectedValue = "AlmacenDefectoAndes";
}
}
Update:
Translating the code above to Visual Basic doesn't work. It throws "System.NullReferenceException: Object reference not set to an instance of an object.."
So. for this to work on Visual Basic, I had to change the code like this:
If DDLAlmacen.Items.Count > 0 Then
If DDLAlmacen.Items.Contains(New ListItem("AlmacenDefectoAndes")) Then
DDLAlmacen.SelectedValue = "AlmacenDefectoAndes"
End If
End If
ListItem item = ddlComputedliat1.Items.FindByText("Amt D");
if (item == null) {
ddlComputedliat1.Items.Insert(1, lblnewamountamt.Text);
}
You could try checking to see if this method returns a null:
if (ddlCustomerNumber.Items.FindByText(GetCustomerNumberCookie().ToString()) != null)
ddlCustomerNumber.SelectedIndex = 0;
//you can use the ? operator instead of if
ddlCustomerNumber.SelectedValue = ddlType.Items.FindByValue(GetCustomerNumberCookie().ToString()) != null ? GetCustomerNumberCookie().ToString() : "0";
If the function return Nothing, you can try this below
if (ddlCustomerNumber.Items.FindByText(
GetCustomerNumberCookie().ToString()) != Nothing)
{
...
}
Sometimes the value needs to be trimmed of whitespace or it won't be matched, in such case this additional step can be used (source):
if(((DropDownList) myControl1).Items.Cast<ListItem>().Select(i => i.Value.Trim() == ctrl.value.Trim()).FirstOrDefault() != null){}

Categories