4

in my WPF application i used to add controls dynamically to a Canvas. The name format of the control is "Control_UniqueValue".

i.e., if i add first control to the canvas, then the name will be "Control_1" and the next will be "Control_2" etc...

my requirement is to get the max value of the added controls

i used the following statement for that

string maxId = (string)canvas1.Children.Cast<FrameworkElement>().ToList().Max(x => (x.Name.Substring(x.Name.LastIndexOf('_') + 1)));

but the problem here is

  1. need to return the value as int

  2. if the canvas contains no controls it will raise error (tried using Nullable type, but failed)

4 Answers 4

4
int  maxId = canvas1.Children
    .Cast<FrameworkElement>()
    .Select(e => int.Parse(e.Name.Substring(e.Name.LastIndexOf('_'))))
    .DefaultIfEmpty()
    .Max();

This should return 0 instead of throwing an exception if there are no elements in the sequence. Also, the call to ToList in your code is not required. This will still throw an exception if any of the control names are not in the expected format.

Sign up to request clarification or add additional context in comments.

Comments

4

To get an integer, you might use the int.Parse method. You don't have to call ToList().

There is no error checking, so your controls have to named regarding to your rule name_id.

You can use the DefaultIfEmpty extension method to provide a default value, if the sequence is empty:

int maxId = canvas1.Children
             .Cast<FrameworkElement>()
             .DefaultIfEmpty(new FrameworkElement() { Name = "Control_0" })
             .Max(x => (int.Parse(x.Name.Split('_')[1]) + 1)));

2 Comments

The type arguments for method 'System.Linq.Enumerable.DefaultIfEmpty<TSource>(System.Collections.Generic.IEnumerable<TSource>, TSource)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
I have edited my answer. You have to provide an instance of the FrameworkElement class as default value for the empty list of cause.
3

Since Max() requires at least one element in its sequence you either have to

  • catch the exception and set max to zero
  • check the length before calling Max(). I'd recommend this

While it is possible to write the logic in one long query expression, I'd recommend you break it up a bit to improve readability. Create a method that returns the integer part of a child control's name:

private int NumberFromElementName(string name)
{
    // Or search for the last '_' using name.LastIndexOf()
    var numString = name.Substring("Control_".Length);
    return Int32.Parse(numString);
}

Then do the query in two steps to allow you to look at the length of the returned sequence. Notice I convert it to an array to avoid having to run the query twice. I'm also using the OfType extension method so that it works even if the canvas has a child which isn't of type FrameworkElement.

var children = canvas1.Children.OfType<FrameworkElement>().ToArray();
int max = 0;
if (children.Length > 0)
    max = children.Max(x => NumberFromElementName(x.Name));
string nextChildName = String.Format ("Control_{0}", max + 1);

EDIT: As pointed out by @Jan in his answer, you can avoid splitting the query in two parts by using the DefaultIfEmpty before the Max call in the query.

Comments

0

got the final answer

int maxId = canvas1.Children .Cast<FrameworkElement>() .Select(e => int.Parse(e.Name.Substring(e.Name.LastIndexOf('_')+ 1))) .DefaultIfEmpty() .Max(x => x + 1); 

Thanks Jan & Lee

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.