user1702369
user1702369

Reputation: 1161

Only set property on one object

How can I make a boolean property like a RadioButton? You know, like a RadioButton, only one can be selected?

Like example below.

When I set one Employee IsResponsiblePerson to true, it should set all others to false. Without using a loop.

var list = new ObservableCollection<Employee>();

public class Employee
{
    public string Name{get;set;}
    public string Surname{get;set;}
    public bool IsResponsiblePerson{get;set;}
}

Upvotes: 8

Views: 745

Answers (3)

Tim Schmelter
Tim Schmelter

Reputation: 460018

This is the ideal use case for a new collection type that inherits from List<Employee> or ObservableCollection<Employee>. There you can encapsulate the whole logic.

Before you should make your Employee class more intelligent by a custom event, the collection class wants to know when the property IsResponsiblePerson was set to true because then it has to change the old responsible. For example like this:

public class Employee
{
    public string Name { get; set; }
    public string Surname { get; set; }

    public event EventHandler ResponsiblePersonChanged;

    private bool _isResponsiblePerson;
    public bool IsResponsiblePerson
    {
        get => _isResponsiblePerson;
        set
        {
            _isResponsiblePerson = value;
            if (_isResponsiblePerson)
            {
                ResponsiblePersonChanged?.Invoke(this, EventArgs.Empty);
            }
        }
    }
}

Now the collection class could be implemented like following, it handles every Employee's ResponsiblePersonChanged event:

public class EmployeeCollection : ObservableCollection<Employee>
{
    public Employee Responsible { get; private set; }

    public EmployeeCollection():base(){}
    public EmployeeCollection(IEnumerable<Employee> employees) : base()
    {
        foreach (Employee e in employees)
        {
            if (e.IsResponsiblePerson)
            {
                if(Responsible != null)
                    throw new ArgumentException("Multiple responsible employees aren't allowed", nameof(employees));
                Responsible = e;
            }
            base.Add(e);
        }
    }

    public new void Add(Employee emp)
    {
        base.Add(emp);
        if (emp.IsResponsiblePerson)
        {
            MakeResponsible(emp);
        }
        emp.ResponsiblePersonChanged -= ResponsibleChanged;
        emp.ResponsiblePersonChanged += ResponsibleChanged;
    }

    private void ResponsibleChanged(Object sender, EventArgs e)
    {
        MakeResponsible(sender as Employee);
    }

    private void MakeResponsible(Employee employee)
    {
        if(!employee.IsResponsiblePerson)
            throw new ArgumentException("Employee is not responsible but should be", nameof(employee));

        if (Responsible != null && !Responsible.Equals(employee))
            Responsible.IsResponsiblePerson = false;
        Responsible = employee;
    }
}

An example:

var list = new EmployeeCollection();
list.Add(new Employee { Name = "1", IsResponsiblePerson = true });
list.Add(new Employee { Name = "2", IsResponsiblePerson = false });
list.Add(new Employee { Name = "3", IsResponsiblePerson = false });
list.Add(new Employee { Name = "4", IsResponsiblePerson = false });
list.Add(new Employee { Name = "5", IsResponsiblePerson = false });

list.Last().IsResponsiblePerson = true;  // now the first employee's IsResponsiblePerson is set to false

Upvotes: 4

Pikoh
Pikoh

Reputation: 7703

What i'd probably do if I didn`t want to use loops, is, as Lasse V. Karlsen says in a comment, store instead the name of the "ResponsiblePerson" in another property:

static string ResponsiblePerson {get;set;}

And change the IsResponsiblePerson property to something like this:

public bool IsResponsiblePerson 
{ 
    get 
    { 
        return this.Name == ResponsiblePerson; 
    }
    set 
    {   
        if (value)
        {
             ResponsiblePerson = this.Name;
        }
        else
        {
            if (this.Name == ResponsiblePerson)
            {
                ResponsiblePerson = "";
            }
        }
    }
}

Sample code:

List<Employee> employees = new List<Employee>() { new Employee() { Name = "name1" },
                                                    new Employee() { Name = "name2" },
                                                    new Employee() { Name = "name3" } };

Employee emp1 = employees.Where(x => x.Name == "name1").First();
emp1.IsResponsiblePerson = true;

Employee emp2 = employees.Where(x => x.Name == "name2").First();
emp2.IsResponsiblePerson = true;

foreach (Employee e in employees) 
{ 
     Console.WriteLine(e.IsResponsiblePerson); //false true false
}

I've made a DotNetFiddle sample here

Upvotes: 5

Dan Rayson
Dan Rayson

Reputation: 1417

Unfortunately, your example gets close to being able to handle the "changed" event for the boolean, but not quite. Your example only allows handling of changes to the collection rather than changes to one of its internal elements.

To do that, you'd need to implement INotifyPropertyChanged, here's an example. Though bare in mind there's a million Notification mechanisms so do some research on it.

To answer your question ("How do I update other members of a collection when an element's property changes") you'd need to handle the changed event for the specific property, in your case IsResponsiblePerson. Then, in that handler, you can use Linq (which internally uses loops), but whatever happens, you will ALWAYS have to perform an operation on the entire list (maybe could break the loop early) in order to achieve what you're asking.

Another concept to look into is ObservableObject, very closely related to INotifyPropertyChanged.Here's the doc on it

Upvotes: 1

Related Questions