views:

862

answers:

2

Hi,

I've created a combobox and have bound it to an observableCollection.

Something like myCmbBox.ItemsSource = myObsCollObj

My scenario is onLoad of the application I shall populate my observableCollection with some values. Now, my UI also gets refreshed with those values automatically. If the user selects a different value from another combobox (say combobox2) I now need to clear all the existing items from myCmbBox. I tried to do so by using the following options

myObsCollObj.Remove() myObsCollObj.RemoveAt() myObsCollObj.Clear() (myCmbBox.ItemsSource as ObservableCollection).Remove()

myCmbBox = null; myCmbBox.Items.Clear()

None of the above options worked and I got an exception saying "Value Cannot be null".

My question is now, how do I clear my collection and add new values (which should refresh my UI as well)

Thanks, Ranjith

Updating with code

XAML

<Page x:Class="ThrowAwayProto.Page1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Page1">
<StackPanel>
    <Label HorizontalAlignment="Center">Environment Modules</Label>
    <ComboBox Name="lstEnv" SelectionChanged="lstEnv_SelectionChanged"></ComboBox>

    <Label HorizontalAlignment="Center">Module Ids</Label>
    <ComboBox Name="cmbModId" SelectionChanged="cmbModId_SelectionChanged"></ComboBox>

    <Label HorizontalAlignment="Center">Environment Modules</Label>
    <ListBox Name="lstSites"></ListBox>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="600"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <Grid Grid.Column="0">
            <Grid.RowDefinitions>
                <!-- Recipe Type -->
                <RowDefinition Height="30"/>
                <!-- Recipe Mode -->
                <RowDefinition Height="30"/>
                <!-- Include expired recipe -->
                <RowDefinition Height="30"/>
                <!-- Search for promoted recipe -->
                <RowDefinition Height="30"/>
                <!-- Site -->
                <!--<RowDefinition Height="30"/>-->
                <!-- Activity -->
                <!--<RowDefinition Height="30"/>-->
            </Grid.RowDefinitions>

            <StackPanel Orientation="Horizontal" Grid.Row="0" Width="100">
                <Label>Recipe Type</Label>
                <ComboBox Name="cmbRecipeType" SelectionChanged="cmbRecipeType_SelectionChanged"/>
            </StackPanel>

            <StackPanel Orientation="Horizontal" Grid.Row="1">
                <Label>Recipe Mode</Label>
                <ComboBox Name="cmbRecipeMode">
                    <ComboBoxItem>PRODUCTION</ComboBoxItem>
                    <ComboBoxItem>ENGINEERING</ComboBoxItem>
                </ComboBox>
            </StackPanel>

            <StackPanel Orientation="Horizontal" Grid.Row="2">
                <Label>Include Expired Recipe?</Label>
                <RadioButton>Yes</RadioButton>
                <RadioButton>No</RadioButton>
            </StackPanel>

            <StackPanel Orientation="Horizontal" Grid.Row="3">
                <Label>Search for promoted Recipe?</Label>
                <CheckBox Name="chkPromRecipe"/>
            </StackPanel>

            <!--<StackPanel Orientation="Horizontal" Grid.Row="4">
                <Label>Site</Label>
                <ComboBox Name="cmbSite"/>
            </StackPanel>

            <StackPanel Orientation="Horizontal" Grid.Row="5">
                <Label>Activity</Label>
                <ComboBox Name="cmbActivity"/>
            </StackPanel>-->                

        </Grid>

        <Grid Name="grdCol2" Grid.Column="1">

        </Grid>
    </Grid>

    <Button Name="btnSearch" Click="btnSearch_Click">Search</Button>

</StackPanel>

Code Behind

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Xml.Linq; using Intel.ATTD.Data; using Intel.Auto.MW.Frameworks; using System.Collections.ObjectModel;

namespace ThrowAwayProto { /// /// Interaction logic for Page1.xaml /// public partial class Page1 : Page { private string _types;

    private Dictionary<string,string> _envList;
    private Dictionary<string, string> _modNameToId;
    private Dictionary<string, string> _selCritNameToId;
    private List<string> _selCritNames;
    private Dictionary<string, ObservableCollection<string>> _selCritNameToList;

    public ObservableCollection<string> _modIdList { get; set; }
    public ObservableCollection<string> _siteList { get; set; }
    public ObservableCollection<string> _recipeTypes { get; set; }

    public Page1()
    {
        InitializeComponent();
        _envList = new Dictionary<string, string>();
        _modIdList = new ObservableCollection<string>();
        _siteList = new ObservableCollection<string>();
        _modNameToId = new Dictionary<string, string>();
        _recipeTypes = new ObservableCollection<string>();
        _selCritNames = new List<string>();
        _selCritNameToId = new Dictionary<string, string>();
        _selCritNameToList = new Dictionary<string, ObservableCollection<string>>();

        cmbModId.ItemsSource = _modIdList;
        lstSites.ItemsSource = _siteList;

        LoadEnvModule();
        GetSites();
    }

    /// <summary>
    /// First step in ASNC
    /// </summary>
    private void LoadEnvModule()
    {

        string environments = ConsoleApplication1.Program.CallEnvironMentModuleMgntService_GetAllEnvironments();

        // Load Xml
        XElement document = XElement.Parse(environments);

        IEnumerable<XElement> childElements =
            from envModule in document.Elements("Environment")
            select envModule;

        foreach (XElement el in childElements)
            _envList.Add(el.Element("Name").Value, el.Element("Id").Value);

        List<string> envNames = new List<string>();
        foreach (string names in _envList.Keys)
            envNames.Add(names);

        // Set the ListBox value
        lstEnv.ItemsSource = envNames;
    }

    /// <summary>
    /// Step 2
    /// </summary>
    private void GetModuleByEnvId()
    {
        string selectedEnvId = lstEnv.SelectedItem as string;
        selectedEnvId = _envList[selectedEnvId];

        DataCollection reqDC = new DataCollection("GetModuleByEnvID");
        reqDC.add(new Parameter("Idsid", "rchevan"));            
        reqDC.add(new Parameter("Id", selectedEnvId));

        Request reqObj = new Request("Dummy", "Dummy");
        reqObj.add(reqDC);

        Request[] requests = new Request[1];
        requests[0] = reqObj;

        DataCollection[] reply = InvokeATMIA(requests, "EnvModuleMgntService", "GetModuleByEnvID");

        string modules = reply[0].toXML();
        // Extract required info
        // Load Xml
        XElement document = XElement.Parse(modules);

        IEnumerable<XElement> childElements =
            from mod in document.Elements("Module")
            select mod;

        if (_modIdList.Count > 0)
        {
            _modNameToId.Clear();
            _modIdList.Clear();
        }

        foreach (XElement el in childElements)
        {                
            _modNameToId.Add(el.Element("Name").Value, el.Element("Env_Mod_Id").Value);
            _modIdList.Add(el.Element("Name").Value);
        }

        // Rebind when a new environment is selected

    }

    private static DataCollection[] InvokeATMIA(Request[] requests, string serName, string opName)
    {
        DataCollection[] reply = null;
        ATMIAProxy proxy = new ATMIAProxy();

        ATMIAMessage input = new ATMIAMessage();
        input.AddParameter("ServiceName", serName);
        input.AddParameter("OperationName", opName);
        input.AddObject(requests);

        ATMIAMessage output = proxy.InvokeMethod(input);

        if (output.GetTxnSuccess())
        {
            return output.GetBody<DataCollection[]>();
        }

        return null;
    }

    private void lstEnv_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {            
        GetModuleByEnvId();
    }

    /// <summary>
    /// Call GetModuleAliasData
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void cmbModId_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        // Get Module Alias data
        // This is the actual search recipe
        string selectedModId = cmbModId.SelectedItem as string;

        //string selectedModId = "DEFLUX";
        selectedModId = _modNameToId[selectedModId];

        DataCollection reqDC = new DataCollection("GetModuleAliasData");
        reqDC.add(new Parameter("Env_Mod_Id", selectedModId));
        reqDC.add(new Parameter("Idsid", "rchevan"));

        Request reqObj = new Request("Dummy", "Dummy");
        reqObj.add(reqDC);

        Request[] requests = new Request[1];
        requests[0] = reqObj;

        DataCollection[] reply = InvokeATMIA(requests, "EnvModuleMgntService", "GetModuleAliasData");

        // Load the Recipe Types and SelCritNames here
        _types = reply[0].toXML();
        // Extract required info
        // Load Xml
        XElement document = XElement.Parse(_types);

        IEnumerable<XElement> childElements =
            from mod in document.Elements("RecipeType")
            select mod;

        foreach (XElement el in childElements)
            _recipeTypes.Add(el.Element("RecipeTypeName").Value);

        cmbRecipeType.ItemsSource = _recipeTypes;


    }

    private void GetSites()
    {
        DataCollection reqDC = new DataCollection("GetEnvToSite");
        reqDC.add(new Parameter("Idsid", "rchevan"));

        Request reqObj = new Request("Dummy", "Dummy");
        reqObj.add(reqDC);

        Request[] requests = new Request[1];
        requests[0] = reqObj;

        DataCollection[] reply = InvokeATMIA(requests, "EnvModuleMgntService", "GetEnvToSite");

        string sites = reply[0].toXML();
        // Extract required info
        // Load Xml
        XElement document = XElement.Parse(sites);

        IEnumerable<XElement> childElements =
            from mod in document.Elements("Environment").Elements("Site")
            select mod;

        foreach (XElement el in childElements)
            _siteList.Add(el.Element("SiteName").Value);

    }

    private void GetSearchAttributes()
    {
        string selectedModId = cmbModId.SelectedItem as string;
        //string selectedModId = "DEFLUX";
        selectedModId = _modNameToId[selectedModId];

        DataCollection reqDC = new DataCollection("GetSearchAttributes");
        reqDC.add(new Parameter("Env_Mod_Id", selectedModId));
        reqDC.add(new Parameter("EntityRequired", "N"));
        reqDC.add(new Parameter("Idsid", "rchevan"));

        Request reqObj = new Request("Dummy", "Dummy");
        reqObj.add(reqDC);

        Request[] requests = new Request[1];
        requests[0] = reqObj;

        DataCollection[] reply = InvokeATMIA(requests, "RecipeSearchService", "GetSearchAttributes");

        string selCrits = reply[0].toXML();
        // Extract required info
        // Load Xml
        XElement document = XElement.Parse(selCrits);

        IEnumerable<XElement> childElements =
            from mod in document.Elements("Sel_Crit_Ids")
            select mod;

        if (_selCritNameToId.Count > 0)
            _selCritNameToId.Clear();

        foreach (XElement el in childElements)
            _selCritNameToId.Add(el.Element("Sel_Crit_Name").Value, el.Element("Sel_Crit_Id").Value);

        GetOptSearchAttributes();

    }

    private void GetOptSearchAttributes()
    {
        DataCollection reqDC = new DataCollection("GetSearchAttributes");
        reqDC.add(new Parameter("Idsid", "rchevan"));

        DataCollection selCritDC = new DataCollection("Sel_Crit_Ids");
        foreach (string selCritName in _selCritNames)
        {
            string selCritId = _selCritNameToId[selCritName];

            selCritDC.add(new Parameter("Sel_Crit_Id", selCritId));
            reqDC.add(selCritDC);
        }

        Request reqObj = new Request("Dummy", "Dummy");
        reqObj.add(reqDC);

        Request[] requests = new Request[1];
        requests[0] = reqObj;

        DataCollection[] reply = InvokeATMIA(requests, "RecipeSearchService", "GetOptSearchAttributeValues");

        string instSelCrits = reply[0].toXML();
        // Extract required info
        // Load Xml
        XElement document = XElement.Parse(instSelCrits);

        IEnumerable<XElement> childElements =
            from mod in document.Elements("InstanceForSelCrit")
            select mod;

        //if (_selCritNameToId.Count > 0)
        //    _selCritNameToId.Clear();

        foreach (string selCrit in _selCritNames)
        {
            string selCritId = _selCritNameToId[selCrit];

            ObservableCollection<string> selCritNameList = new ObservableCollection<string>();
            _selCritNameToList.Add(selCrit, selCritNameList);

            foreach (XElement el in childElements)
            {
                string selCritRecv = el.Element("Sel_Crit_Id").Value;

                if (string.Equals(selCritId, selCritRecv))
                {
                    string selCritName = el.Element("Sel_Crit_Inst_Name").Value;
                    selCritNameList.Add(selCritName);
                }
            }
        }

        GridLengthConverter myGridLengthConverter = new GridLengthConverter();
        GridLength gl1 = (GridLength)myGridLengthConverter.ConvertFromString("50");

        // Populate values to the right hand side of the 
        // search screen
        int counter = 0;
        foreach (string selCrit in _selCritNames)
        {
            ComboBox cmbBox = new ComboBox();
            cmbBox.ItemsSource = _selCritNameToList[selCrit];
            Grid.SetRow(cmbBox, counter);
            Grid.SetColumn(cmbBox, 1);

            grdCol2.Children.Add(cmbBox);

            ++counter;
        }

    }

    /// <summary>
    /// Based on the Recipe Type selected load other information
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void cmbRecipeType_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        string selectedRecipeType = cmbRecipeType.SelectedItem as string;

        // Retrieve all Sel_Crit_Names to display
        XElement document = XElement.Parse(_types);

        IEnumerable<XElement> childElements =
            from mod in document.Elements("RecipeType")
            select mod;

        GridLengthConverter myGridLengthConverter = new GridLengthConverter();
        GridLength gl1 = (GridLength)myGridLengthConverter.ConvertFromString("50");

        // Clear before adding new columns
        grdCol2.ColumnDefinitions.Clear();
        grdCol2.RowDefinitions.Clear();

        ColumnDefinition colDef1 = new ColumnDefinition();
        ColumnDefinition colDef2 = new ColumnDefinition();
        grdCol2.ColumnDefinitions.Add(colDef1);
        grdCol2.ColumnDefinitions.Add(colDef2);

        int counter = 0;

        if (_selCritNames.Count > 0)
            _selCritNames.Clear();

        foreach (XElement el in childElements)
        {
            string recipeType = el.Element("RecipeTypeName").Value;

            if (string.Equals(selectedRecipeType, recipeType))
            {
                IEnumerable<XElement> aliases =
                from mod in el.Elements("Aliases")
                select mod;

                foreach (XElement al in aliases)
                {
                    string element = al.Element("Sel_Crit_Name").Value;

                    _selCritNames.Add(element);

                    Label label = new Label();
                    label.Content = element;

                    RowDefinition row = new RowDefinition();
                    grdCol2.RowDefinitions.Add(row);
                    grdCol2.RowDefinitions[counter].Height = gl1;
                    Grid.SetRow(label, counter);
                    Grid.SetColumn(label, 0);

                    grdCol2.Children.Add(label);

                    ++counter;
                }
                break;
            }

        }



        // Now fill the values for the fields retrieved above 
        // in the loop
        GetSearchAttributes();
    }

    private void btnSearch_Click(object sender, RoutedEventArgs e)
    {
        DataCollection reqDC = new DataCollection("SearchLinkRecipe");
        reqDC.add(new Parameter("Env_Mod_Id", cmbModId.SelectedItem as string));
        reqDC.add(new Parameter("RecipeType", cmbRecipeType.SelectedItem as string));
        reqDC.add(new Parameter("RecipeMode", "PRODUCTION"));
        reqDC.add(new Parameter("ExpiredRecipe", "N"));
        reqDC.add(new Parameter("Site", "CHANDLER, ARIZONA"));
        reqDC.add(new Parameter("PromotedStatus", "N"));
        reqDC.add(new Parameter("SearchType", "Dummy"));
        reqDC.add(new Parameter("LastUpdatedBy", "rchevan"));

        DataCollection keyDC1 = new DataCollection("Key");
        keyDC1.add(new Parameter("KeyCriteria", _selCritNames[0]));
        keyDC1.add(new Parameter("KeyValue", "*"));
        reqDC.add(keyDC1);

        DataCollection keyDC2 = new DataCollection("Key");
        keyDC2.add(new Parameter("KeyCriteria", _selCritNames[1]));
        keyDC2.add(new Parameter("KeyValue", "*"));
        reqDC.add(keyDC2);

        //DataCollection keyDC3 = new DataCollection("Key");
        //keyDC3.add(new Parameter("KeyCriteria", "OPERATIONGROUP"));
        //keyDC3.add(new Parameter("KeyValue", "*"));
        //reqDC.add(keyDC3);

        Request reqObj = new Request("Dummy", "Dummy");
        reqObj.add(reqDC);

        Request[] requests = new Request[1];
        requests[0] = reqObj;

        DataCollection[] reply = InvokeATMIA(requests, "RecipeSearchService", "SearchLinkRecipe");
    }

}

}

+1  A: 

It sounds like your ComboBox has its SelectedItem or SelectedValue property bound to something that isn't allowed to be null. So when you try to clear the collection that the ComboBox's Items property is bound to, it's trying to assign "null" to that property and an exception is being raised.

There's probably a really nice WPF-y way to get around it. I'd probably start by trying to temporarily suspend the binding by setting its update mode to Explicit and then clear the collection:

var binding = BindingOperations.GetBinding(myCmdBox, ComboBox.SelectedItemProperty);
binding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
myObservableCollection.Clear();
// repopulate the collection here
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

That's just a suggestion. I might be barking up the wrong tree but it might give you some clues as to why you're getting the error.

Update

Wow - that's a lot of code. Shame you couldn't reduce it down to the bare minimum necessary to reproduce the problem.

I'm assuming your exception is being thrown from within the cmbRecipeType_SelectionChanged event handler. If that's the case, you have two options:

  1. Put a breakpoint inside that method and find out what's throwing the exception. It's probably something simple, like this first line yielding a null value:

    string selectedRecipeType = cmbRecipeType.SelectedItem as string;

If that's true, you could probably whack this in straight after that:

if (selectedRecipeType == null) return;

Which will prevent any subsequent code from accessing selectedRecipeType as if it's non-null.

  1. Do something similar to what I originally suggested, and temporarily unhook that event handler from your ComboBox before you clear the items. Then hook it up again straight after.
Matt Hamilton
Hi,I'm not binding the selectedItem or SelectedValue property anywhere. I do something like myCmbBox.ItemsSource = myObsCollObj; only once during app startup. Then all that I do is populate my observable collection which automatically refreshes my view as well. Now when I want to clear all existing values from the observable collection I get an exception.
Jithu
Actually you are right.. I'm using the selectedItem value from the combo box when user selects a value from the combobox. If I remove that and then try to clear() it works. Now the question is how do I get the selectedItem?
Jithu
Update your question with your new findings and include some more code so we can see what you're doing.
Matt Hamilton
Hi Matt, I have included the entire code. Please have a look and let me know.
Jithu
Hi, I found a way to get over this problem. I used try, catch block wherever I used the selectedItem. All that I do in the catch block is just a return and log. Do you think it makes sense to use this way?-jithu
Jithu
A: 

Some people use the dummies approach "get out of the car" and enter into the car again:

myCmbBox.ItemsSource = null;
myCmbBox.ItemsSource = yourLoadMethod();
Junior Mayhé