I have a ComboBox on a WPF application that contains all the printers attached to a computer. The printers are getting to the ComboBox correctly and I am also capturing the default printer. Now what I want to do is set the default or selected value on the ComboBox to the default printer.
I am getting the list of printers and the default with this
private void GetPrinterList()
{
var server = new PrintServer();
var queues = server.GetPrintQueues(new[] {EnumeratedPrintQueueTypes.Shared,
EnumeratedPrintQueueTypes.Connections});
string defaultPrinter = GetDefaultPrinter();
Printers = new ObservableCollection<Printer>();
foreach (var item in queues)
{
Printers.Add(new Printer { Name = item.FullName });
}
queues = server.GetPrintQueues(new[] { EnumeratedPrintQueueTypes.Local });
foreach (var item in queues)
{
Printers.Add(new Printer { Name = item.FullName });
}
var defPrinter = Printers.FirstOrDefault(i => i.Name == defaultPrinter);
if (defPrinter != null)
{
//NOTE Modified example after posting because #ASh
//made aware (in comments) I had an error in code.
CurrentDefaultPrinter = defPrinter.Name.ToString();
}
}
On the XAML side I have tried binding both the SelectedValue and SelectedValuePath to the CurrentDefaultPrinter but neither show the selected value in the ComboBox. What am missing to make this work?
<ComboBox
Width="150"
Height="35"
Margin="0,0,0,2"
DisplayMemberPath="Name"
FontSize="18"
SelectedItem="{Binding CurrentDefaultPrinter, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
ItemsSource="{Binding Printers}" />
I did find this solution that is similar to what I am trying to do but isn't close enough for me to solve my issue.
Setting a default selected item in ComboBox in WPF MVVM application
Note Both my code and XAML have been modified after posting because it was pointed out there was an error in my code. However, after the modification I am still not getting the ComboBox to show the default printer with updated code.
don't bind SelectedValue or SelectedValuePath, only SelectedItem:
SelectedItem="{Binding CurrentDefaultPrinter}"
note that SelectedItem should be present in ItemsSource collection. so it should have the same type as items in that collection, not string. additionally it should raise PropertyChanged event (from INPC interface)
public Printer CurrentDefaultPrinter { get; set; }
CurrentDefaultPrinter = Printers.FirstOrDefault(i => i.Name == defaultPrinter);
Related
Hi I am trying to add list items to an observable collection list.
I have a model where I setup a list property
public class DisplayList
{
public List<string> listItem { get; set; }
}
then on my main page I have an observable collection
private ObservableCollection<DisplayList> ListDisplay;
which I instantiate on page load
public MainPage()
{
this.InitializeComponent();
location = new ObservableCollection<storeLocations>();
ListDisplay = new ObservableCollection<DisplayList>();
// location = manager.getStoreLocations();
var dbList = db.Bales.Where(b => b.Location != null).Select(b => b.Location).ToList();
InitialLoad(dbList, null);
}
I am using suggestion boxes and want to filter results based on the selection made. The filtered results then display in a list on screen and this is where I am having a bit of trouble. I get it to display on screen, but it is displaying
System.Collection.Generic.List'1[S....... instead of the actual item in the list.
I am thinking I am not enumerating properly, but cant seem to pin point the error in my ways.
This is the method that is meant to populate the list based on selection of suggestion box.
public ObservableCollection<DisplayList>BaleList(List<string> CatNo)
{
foreach (var item in CatNo)
{
ListDisplay.Add(new DisplayList {listItem = CatNo.ToList()});
}
lstBales.IsItemClickEnabled = true;
return ListDisplay;
}
it takes in a parameter of type list which is gotten from the suggestion box. so the parameter value is basically what I want to display in the list on screen. e.g. CP1354-2 and second item CP1355-3 So those values come into the method. I want to apply those values to the observable collection as the listbox control is bound to the observable collection.
EDIT
adding binding in XAML
<ListView x:Name="lstBales" ItemsSource="{x:Bind ListDisplay}">
<ListView.ItemTemplate>
<DataTemplate x:Name="TemplateListName" x:DataType="data:DisplayList">
<Grid>
<TextBlock Text="{x:Bind listItem}"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ListDisplay.Add(new DisplayList {listItem = CatNo.ToList()});
and <TextBlock Text="{x:Bind listItem}"/>
You are binding a list to a TextBlock due to which ToString()is implicitly called and you don't get the actual values. According to your requirements, you can either change listItem to a string or create a nested ListView
I have two comboboxes: Categories and Types. When my form is initially displayed, I am listing all categories and types that exist in the database. For both comboboxes, I manually insert row 0 to be of value “All” so that they don’t have to choose either if they don’t want to.
I have both comboboxes bound to ReactiveObjects so that if the user selects a Category, the Types combobox is automatically re-populated with a query to show only types relevant to the selected Category along with the row 0 added.
When the user selects a Category, it runs the query properly, returns the relevant Types, adds the row 0 properly and the combobox is populated correctly; however, on the XAML size it’s not selecting the row 0, and it adds the red outline around the combobox signifying an invalid selection was made.
If no choice is made for the Type combobox and the form is submitted, the correct value of 0 is passed. So while everything is working properly, the red box around the Types combobox is communicating to the user that they did something wrong and I cannot determine why the XAML isn’t picking up the selected values. I have run the code without it adding the row 0 and it still has the same behavior, i.e., the combobox is populated correctly, but no row is selected and the red outline appears.
XAML for comboboxes
<ComboBox
Grid.Row="3"
Grid.Column="1"
Width="200"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Style="{StaticResource SimpleComboBox}"
ItemsSource="{Binding Categories}"
SelectedValue="{Binding SearchCriteria.CategoryID}"
SelectedValuePath="ComboValueID"
DisplayMemberPath="ComboDataValue"
/>
<TextBlock
Grid.Row="3"
Grid.Column="2"
Style="{StaticResource NormalTextNarrow}"
Text="Type" VerticalAlignment="Top"
/>
<ComboBox
Grid.Row="3"
Grid.Column="3"
Width="200"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Style="{StaticResource SimpleComboBox}"
ItemsSource="{Binding Types}"
SelectedValue="{Binding SearchCriteria.TypeId}"
SelectedValuePath="ComboValueID"
DisplayMemberPath="ComboDataValue"
/>
Relevant VM code
// Definition of SearchCriteria. ResourceItem is a ReactiveObject and
// all of the relevant properties watch for changes in values.
private ResourceItem searchCriteria;
public ResourceItem SearchCriteria
{
get { return searchCriteria; }
set { this.RaiseAndSetIfChanged(ref searchCriteria, value); }
}
// This all happens in my constructor
// Defining Row 0
var b = new GenericCombobox { ComboValueID = 0, ComboDataValue = "All" };
// Populating the comboboxes from the database
Categories = omr.GetKTValues("RES_CATEGORIES");
Types = omr.GetKTValuesRU("RES_TYPES");
// Adding the row 0
Categories.Insert(0, b);
Types.Insert(0, b);
// The form is displayed correctly at this point with the row 0 selected
Problem Code
// When the user picks a category, this is the method that is invoked:
private void categoryChanged()
{
if (SearchCriteria.CategoryID != 0)
{
Types = rr.GetCategoryTypes(SearchCriteria.CategoryID);
SearchCriteria.TypeId = 0;
}
}
// This runs correctly and returns the relevant Types
public List<GenericCombobox> GetCategoryTypes(int categoryId)
{
string sql = "res.usp_GetCategoryTypes";
var types = new List<GenericCombobox>();
SqlConnection sqlCn = DatabaseCommunication.OpenConnection();
using (SqlCommand cmd = new SqlCommand(sql, sqlCn))
{
// Omitting db stuff for brevity...
try
{
SqlDataReader dr = cmd.ExecuteReader();
while (dr.Read())
{
types.Add(new GenericCombobox
{
ComboValueID = (int)dr["TypeId"],
ComboDataValue = (string)dr["Type"],
IsSelected = false,
Description = (string)dr["Type"],
ComboDataCode = (string)dr["Type"]
});
}
// More db-stuff omitted
}
// Adding the row 0
var b = new GenericCombobox { ComboValueID = 0, ComboDataValue = "All", IsSelected = false, Description = "All", ComboDataCode = "All" };
types.Insert(0, b);
return types;
}
Update with Additional Code
// Object containing the TypeId property
public class ResourceItem : ReactiveObject, ISelectable
{
public int Id { get; set; }
public int? OriginalItemId { get; set; }
// ...many other properties...
private int typeId;
public int TypeId
{
get { return typeId; }
set { this.RaiseAndSetIfChanged(ref typeId, value); }
}
// ...and many more follow...
I've been able to reproduce the issue, and I've found a few silly things I can do that make it stop happening.
If I select an item in Types other than "All", then when I change the selection in Category, it selects "All" in Types.
It also works if, in categoryChanged(), I replace this line...
SearchCriteria.TypeId = 0;
with this one:
SearchCriteria = new ResourceItem() {
TypeId = 0,
CategoryID = SearchCriteria.CategoryID
};
If I have ResourceItem.TypeId.set raise PropertyChanged regardless of whether the value has changed or not, it again works correctly.
My hypothesis is that the SelectedItem in the Types combobox (which you aren't even using!) is not changing when the collection changes, because you're not telling it to update SelectedValue.
Setting SearchCriteria.TypeId = 0 is a no-op when SearchCriteria.TypeId is already equal to zero, because RaiseAndSetIfChanged() does just what the name says: It checks to see if the value really changed, and if it hasn't, it doesn't raise PropertyChanged.
SelectedValue happens by chance to be the same value as the new "All" item has, but the combobox doesn't care. It just knows that nobody told it to go find a new SelectedItem, and the old one it has is no good any more because it's not in ItemsSource.
So this works too:
private void categoryChanged()
{
if (SearchCriteria.CategoryID != 0)
{
Types = rr.GetCategoryTypes(SearchCriteria.CategoryID);
SearchCriteria.SelectedType = Types.FirstOrDefault();
//SearchCriteria.TypeId = 0;
//SearchCriteria = new ResourceItem() { TypeId = 0, CategoryID = SearchCriteria.CategoryID };
}
}
XAML:
<ComboBox
Grid.Row="3"
Grid.Column="3"
Width="200"
HorizontalAlignment="Left"
VerticalAlignment="Top"
ItemsSource="{Binding Types}"
SelectedValue="{Binding SearchCriteria.TypeId}"
SelectedItem="{Binding SearchCriteria.SelectedType}"
SelectedValuePath="ComboValueID"
DisplayMemberPath="ComboDataValue"
/>
class ResourceItem
private GenericCombobox selectedType;
public GenericCombobox SelectedType
{
get { return selectedType; }
set { this.RaiseAndSetIfChanged(ref selectedType, value); }
}
I think your best bet is my option #2 above:
private void categoryChanged()
{
if (SearchCriteria.CategoryID != 0)
{
Types = rr.GetCategoryTypes(SearchCriteria.CategoryID);
SearchCriteria = new ResourceItem() {
TypeId = 0,
CategoryID = SearchCriteria.CategoryID
};
// Do you need to do this?
// SearchCriteria.PropertyChanged += SearchCriteria_PropertyChanged;
}
}
The potential problem here is that, in my testing code I called categoryChanged() from a PropertyChanged handler on SearchCriteria. If I create a new SearchCriteria, I need to make sure I handle that event on the new one.
Given that, maybe binding SelectedItem on the Types combobox is the best solution after all: It's the only one I can think of that doesn't require the viewmodel to do strange things to make up for misbehavior in the view that it really shouldn't be aware of.
I have a weird problem with something simple I suppose.
I have a combobox with two bindings set up - one for ItemsSource and another for SelectedItem.
The selected item is not working on initial startup, but then it works OK. Output does not indicate any binding problems, I have also set up a TextBlock with the same binding to see if it works - and it does.
Here's the code
<ComboBox IsSynchronizedWithCurrentItem="True" IsEditable="False"
Name="ProgramsCollectionComboBox"
SelectedItem="{Binding ElementName=ThisUc,
Path=SelectedProgram}"
ItemsSource="{Binding ElementName=ThisUc,
Path=ProgramsCollection}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Text="{Binding ElementName=ThisUc,
Path=SelectedProgram.Name, Mode=TwoWay}" />
The property:
private Program _selectedProgram;
public Program SelectedProgram
{
get
{
if (_selectedProgram == null)
{
_selectedProgram = new Program(Settings.Default.SelectedProgramPath);
}
return _selectedProgram;
}
set
{
_selectedProgram = value;
Settings.Default.SelectedProgramPath = SelectedProgram.PathProgramFolder;
RaisePropertyChanged("SelectedProgram");
}
}
It saves and reads the settings OK, the initial values is shown in the textblock below the combobox, when I change the selected item, the textblock is updated, the settings are changed and everything works fine - except for the fact that on app startup, selected item is not selected.
Thanks for help!
There are two reasons your initial binding is not working. First, as Jehof has mentioned himself, is the fact that you're setting your SelectedProgram to an item that is not part of the ProgramsCollection.
Furthermore, when you are setting the initial value of your SelectedProgram you are doing so in the getter, where PropertyChanged is not invoked and thus the binding will never be aware of that change. You can either invoke PropertyChanged when initializing it in the getter:
...
get
{
if (_selectedProgram == null)
{
_selectedProgram = _programsCollection?.FirstOrDefault();
RaisePropertyChanged("SelectedProgram");
}
return _selectedProgram;
}
...
Or even better, set the default value on the private field:
private Program _selectedProgram = _programsCollection?.FirstOrDefault();
...
The getter of your property SelectedProgram should return a value of your ProgrammsCollection and not a new instance if it is null.
If the value is not part of the collection that is bound to the combobox it is not displayed.
I have a combo box rigged as
<ComboBox x:Name="HeadComboBox"
ItemsSource="{Binding DataContext.HeadList, RelativeSource={RelativeSource FindAncestor,AncestorType= {x:Type views:FixedAssetBaseWholeUC}}}" Margin="195,78,86,0" VerticalAlignment="Top" SelectedItem="{Binding HeadItem}" DisplayMemberPath="Name" />
The datacontext.HeadList will point to:
public List<FixedAssetHeadItem> HeadList
{
get
{
return _headList;
}
set
{
if (_headList != value)
{
_headList = value;
RaisePropertyChanged("HeadList");
}
}
}
I disable the UserControl in which the combobox rests and load another control to edit the items in the headlist by
DeleteFromHeadList(1);
FixedAssetBaseWholeViewModel fbwvm = (FixedAssetBaseWholeViewModel)Fabwuc.DataContext;
fbwvm.HeadList = HeadList;
When the edit is complete the re enable the usercontrol only to find the selection disappers.
Debug shows
http://postimg.org/image/hdz4h4px3/
How should I deal with this?
You should not bind to List (can cause memory leak), but bind to ObservableCollection<> instead. In this way, your ComboBox should update appropriately. Also your HeadItem should be INPC property - in setter (private or public, depends on your code) should be raising of property changes.
I try to build settings page to my Windows Phone 8 app, the settings page has couple of ListPickers. The basic idea has been taken from here: http://msdn.microsoft.com/en-us/library/windowsphone/develop/ff769510(v=vs.105).aspx
In my settings page xaml I have just declared:
<toolkit:ListPicker x:Name="listPicker1" ExpansionMode="FullScreenOnly" SelectionMode="Single" FullModeItemTemplate="{StaticResource generalListPickerFullTemplate}" ItemTemplate="{StaticResource generalListPickerTemplate}" SelectedItem="{Binding Source={StaticResource appSettings}, Path=listPicker1, Mode=TwoWay}" />
In "code behind", I create list and set it item source to listPicker1
listPicker1List.Add(new ListPickerItem() { name = "First value", value = "value_1" });
listPicker1List.Add(new ListPickerItem() { name = "Second value", value = "value_2" });
this.listPicker1.ItemsSource = listPicker1List;
StaticResource appSettings points to class which is basically similar than in the MS example,
public ListPickerItem listPicker1
{
get
{
return GetValueOrDefault<ListPickerItem>(KeyName, Default);
}
set
{
if (AddOrUpdateValue(KeyName, value))
{
Save();
}
}
}
So is it not possible to set itemsource and use two way bindig? If I set these both, I get System.ArgumentOutOfRangeException.
Basically my only goal is to have listPicker with items, which have text to display for user and value. And to easily set and get these to Isolates storage.
You have this error when you set itemsource or select an item?