I'm writing a windows store app, the main page is a GridView of grouped items.
I already managed to get the templates of the different tiles of the GridView,
but the problem is as follows:
I have bound the ItemsSource to my viewModel's collection that holds 12 items.
there is 2 different problems:
First, how can I make the GridView to always not show the last one or two items, in that way that the there is always a missing tile or two as shown in the picture, although there is "room" for more items in the bounded collection.
Second, I'm using an ItemTemplateSelector for different templates for the items based on an index.
my design is that in every last item I need to select a template without an image(for the example).
How can I get the last visible item in the GridView?
This is my code to create the different tile sizes:
protected override void PrepareContainerForItemOverride(Windows.UI.Xaml.DependencyObject element, object item)
{
try
{
IIndexable viewModel = item as IIndexable;
element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.ColumnSpanProperty, viewModel.Index == 0 ? 2 : 1);
element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.RowSpanProperty, viewModel.Index == 0 ? 2 : 1);
}
catch
{
element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.ColumnSpanProperty, 1);
element.SetValue(Windows.UI.Xaml.Controls.VariableSizedWrapGrid.RowSpanProperty, 1);
}
finally
{
base.PrepareContainerForItemOverride(element, item);
}
}
Thanks.
In order to do that, you'll either have to write your own control or you can have your viewmodel (which should store your itemssource) return a subset of the items.
If you do write your own control, you'll want to override the ArrangeOverride and MeasureOverride methods. You'll then be able to create a dependency property of something like "MaximumColumns". Your Arrange and Measure methods can then determine whether or not to add a child to the tree based on if there's enough room. I suggest creating a two-dimensional array of bools to keep track of which cells are occupied, then having a "Fit" function that attempts to find if there is room for the object you are adding.
For returning the subset of items, you can either have your viewmodel calculate how much space each one will take and return a new collection of the proper items, or you can just hard limit it to a given value.
Related
In the data grid control, there's an empty row at the bottom. When a user fills the cells, I can store the contents, hence creating an additional element in the database.
However, when the number of pre-existing rows grows large, the user'd have to scroll each time to access that row. Is there a smooth way to move it up to the top?
The solution I can think of is placing other controls in a panel right above the data grid. But that's more work than I'm willing to spend. Still, it'd be nice to let the users not be forced to scroll their mouses off.
Assuming the collection view returned by your DataGrid's Items property implements IEditableCollectionView (which, in my experience, does for an editable DataGrid), then you should be able to use the NewItemPlaceholderPosition property through the explicit interface:
// Assume myDataGrid is the DataGrid control holding your results.
// You can do this inside your window/control's constructor after its
// call to InitializeComponent and after myDataGrid's ItemsSource
// property has been set.
var collView = myDataGrid.Items as IEditableCollectionView;
if( collView != null )
collView.NewItemPlaceholderPosition = NewItemPlaceholderPosition.AtBeginning
Here's my problem: I need to make a DataGrid with dynamic comboboxes using the WPF. If the value of a combobox is already used in the previous rows, the next ones, that will be added by the user, shouldn't contain the item already used.
In this image, the ITEM A shouldn't apear on the combobox of the second line.
I don't have ideia how to accomplish this, can anyone show me a light?
OBS: The DataGrid ItemsSource is binded to an ObservableCollection, and the DataGridComboBoxColumn ItemsSource is a List.
Thanks !!!
The ItemsSource of the combo doesn't have to be bound to an ObservableCollection, but it can help depending on exactly how you solve this.
When that cell goes in to edit mode the property the ItemsSource is bound to gets hit - so you can return a new list of items each time the getter is hit. Here is a very basic example to give you an idea:
public List<string> MyItemsSource
{
get
{
var myNewList = MyMasterList.ToList(); //create a (reference) copy of the master list (the items are not copied though, they remain the same in both lists)
if (PropertyA != null)
myNewList.Remove(PropertyA);
return myNewList;
}
}
So what you are creating and returning is a filtered version of your master list of all possible items. LINQ will be of great help to you here.
Alternatively you could keep just one static copy of the master list as an ObservableCollection, and simply remove items from that static copy as they get selected (and add them back in as they get unselected). Which option you choose will depend on how many times the list can be modified due to items being selected and how complicated it is to generate the list. I've used the dynamically generated list many times in the past, it's an option that works well in most cases.
Hello I have an empty DatagridView that is disabled until I start adding rows to it. I have a combo box that has two selectable options that is going to control what information is going to get loaded into it and also vary the columns that are in there. Right now on the SelectionChanged event for the combo box I have something like:
if (defaultComboBox.SelectedIndex == 0)
{
if (extractionDataGrid.Columns.Contains(companyIDColumn))
{
extractionDataGrid.Columns.Remove(companyIDColumn);
extractionDataGrid.Columns.Remove(companyNameColumn);
extractionDataGrid.Columns.Remove(maintenanceColumn);
extractionDataGrid.Columns.Remove(simColumn);
}
extractionDataGrid.Columns.Add(userNameColumn);
extractionDataGrid.Columns.Add(departmentColumn);
extractionDataGrid.Columns.Add(officePhoneColumn);
extractionDataGrid.Columns.Add(officePhontExtColumn);
extractionDataGrid.Columns.Add(otherPhoneColumn);
. [18 columns]
.
.
.
And the opposite for a selected index of 1. There are quite a few columns here and was trying to think of a way to avoid huge blocks of code to initialize the new datagridview columns, adjust the header text, adjust the auto size mode, and various other parameters. Also when I switch back and forth between the selected index, this actually adds the columns in different orders every time. They should be added in the sequence I've set here??
Thanks.
Add all your 0 index columns to a List
Add all your 1 index columns to another List
Pass the list of columns to a new method you create that iterates over the List to add or remove as necessary.
Put a synch lock on your add/remove methods. That is probably what is giving the appearance of different orders due to the
extractionDataGrid.Columns.Remove(companyIDColumn);
i.e. the condition is met before the grid is fully updated.
Alternatively change the conditional so that it is independent of column order and so that it checks a variable that is only set after all the columns have been added and or removed.
i.e.
switch (gridMode){
case GridMode.Company:
this.AddColumns(userColumnList);
gridMode = GridMode.User;
break;
case GridMode.User:
this.AddColumns(userColumnList);
gridMode = GridMode.Company;
break;
}
protected void AddColumns(List<Column> adds){
extractDataGrid.Layout.Suspend();
extractionDataGrid.Columns.Clear();
foreach(Column c in adds){
extractionDataGrid.Columns.Add(c);
}
extractDataGrid.Layout.Resume();
}
Alternatively don't delete columns but rather turn off visibility if that's an option.
I am struggling to figure out the correct control to use for a list of predefined jobs in the included form. I currently have a ListBoxControl in the Predefined Job Name group that lists all of the predefined jobs for a marine service shop (i.e. oil change, tune up, etc...). Then, based on the item (i.e. job name) that is selected in my ListBox, I need to display the items that correspond to that job. For example, if oil change is the selected job I need to show 4 quarts oil, 1 oil filter, labor, etc...and so on.
Currently, when I load the form data I have a DAO that retrieves all of my jobs from the database using LINQ to SQL. Then I iterate over the results and put the job names into the ListBox. The problem that I am having is that there is no tag for ListBox items like there is for ListView items. So each time the user selects another item in the ListBox, I have to perform another LINQ query to get the job from the database again so that I can display its' corresponding items. If I could use a ListView and hide the column header I could set the entire job on the tag so that each time the user selects a new item I would have access to the details without having to make another call to the database. Is there a way that I can hide the column header of a ListView without hiding the entire column?
You can set the HeaderStyle member of the ListView to None.
listView1.HeaderStyle = System.Windows.Forms.ColumnHeaderStyle.None;
Checkout the ListView HeaderStyle property. It has the following options:
None
Nonclickable
Clickable
From MSDN:
The HeaderStyle property allows you to specify whether the column headers are visible or, if they are visible, whether they will function as clickable buttons. If the HeaderStyle property is set to ColumnHeaderStyle.None, the column headers are not displayed, although the items and subitems of the ListView control are still arranged in columns
You can also create simple object like ListItem which has two poperties: Text (string) and Tag (object). Then implement ListItem.ToString() and you can use these in the ListBox as well.
You can also check out Better ListView Express component, which is free and allows displaying items in Details view without columns. The advantage over ListBox and ListView is a native look and many extra features.
Easy way is using the ColumnWidthChanging event
private void listViewExtended1_ColumnWidthChanging(object sender, ColumnWidthChangingEventArgs e)
{
if (e.ColumnIndex == 0)
{
e.Cancel = true;
e.NewWidth = listViewExtended1.Columns[e.ColumnIndex].Width;
}
}
I found that if you know for a fact you are not displaying the headers it may be best to set the HeaderStyle property to None, as Rajesh mentions above.
When setting in the .CS when screen initially loads the headers are displayed until screen is fully rendered.
I have a WPF application with a screen containing a tab control with two tabs. On each tab is a datagrid, each one bound to an ObservableCollection of Part objects. The part has a few "quantity" properties, which need to be synchronized between the grids. For example, if the user changes the quantity of partABC on grid1, partABC either needs to be added to grid2 with the same quantity, or if grid2 already contains partABC, then its quantity must be changed to reflect grid1.
My problem is that this must work in both directions. If I set a PropertyChanged handler on every part in both grids, I end up with an infinite loop as they constantly update each other's quantities. Up until now, I was handling this during the tab control selection changed event, just iterating through one of the lists and setting quantities one-by-one. This worked well enough until I realized that the users could potentially add thousands of parts to their lists, and at that point this process takes an unacceptable amount of time to complete (around 25 seconds for 4500 part objects).
edit
The first grid contains every part in the database, serving as sort of a "pick-list" where users simply scroll to the part they are looking for and enter quantities. The second grid contains only parts which have been manually entered by the user, in the event that they prefer to type in the numbers of the parts they want. Grid2 is always a subset of grid1.
You can accomplish this through databinding. You should not create duplicate Part objects. Instead duplicate the collections that hold the parts.
Part sharedPart = new Part();
Part onlyInTabA = new Part();
Part onlyInTabB = new Part();
ObservableCollection<Part> tabAParts = new ObservableCollection<Part>() { sharedPart, onlyinTabA };
ObservableCollection<Part> tabBParts = new ObservableCollection<Part>() { sharedPar, onlyInTabB };
Now use tabAParts to databind to the grid on tab A and tabBParts to databind to the grid on tab B
If your Part class implements INotifyPropertyChanged then changing a property of sharedPart will update both grids on both tabs. When you add a new part you can choose to make it shared (add it to both collections) or to keep it tab-specific