Programatically bringing a Datagrid row into view in WPF, MVVM - wpf

I would like to bring a row of my data grid into view programatically. I have a more than 100 rows. When I create a row(which I am doing by adding an item to a observable collection) I would like that new row to be selected and bring that into view. I was able to select the new row in my code but could not do the scrolling. More over I want the first cell of the row to be in edit mode so that the user can input text. I am following MVVM pattern for the application and would like to keep zero code in my views. How Can I achieve this?
Any help or suggestion will be appreciated....
Update:
This what I did in my XAML
<telerik:RadGridView ItemsSource="{Binding AllPartClasses}"
SelectedItem="{Binding SelectedPartClassViewModel, Mode=TwoWay}"
SelectionMode="Single" IsSynchronizedWithCurrentItem="True">
in my view model I did this
void AddNewPartClassExecute()
{
PartClass newPartClass = new PartClass();
PartClassViewModel tempPartClass = new PartClassViewModel(newPartClass);
tempPartClass.IsInValid = true;
AllPartClasses.Add(tempPartClass);
SelectedPartClassViewModel = tempPartClass;
Global.DbContext.PartClasses.AddObject(newPartClass);
//OnPropertyChanged("AllPartClasses");
}
public PartClassViewModel SelectedPartClassViewModel
{
get
{
return _selectedPartClassViewModel;
}
set
{
_selectedPartClassViewModel = value;
OnPropertyChanged("SelectedPartClassViewModel");
}
}
It did not work for me.

For the regular WPF DataGrid you can use ScrollIntoView. In your view hookup the SelectionChanged event to the following in your view code-behind cs file.
private void OnSelectionChanged( object sender, SelectionChangedEventArgs e )
{
Selector selector = sender as Selector;
DataGrid dataGrid = selector as DataGrid;
if ( dataGrid != null && selector.SelectedItem != null && dataGrid.SelectedIndex >= 0 )
{
dataGrid.ScrollIntoView( selector.SelectedItem );
}
}

When following MVVM pattern you should not do a UI-specific stuff like scrolling from a code.
Solution would be simple - just bind DataGrid.SelectedItem to a property in ViewModel and when adding a new item in the items collection just update a property bound to SelectedItem so it would reference to just added item and data grid should select an appropriate row automatically.
<DataGrid
ItemsSource="{Binding UnderyingItemsCollection}"
SelectedItem="{Binding RecentlyAddedItem, Mode=TwoWay}"
IsSynchronizedWithCurrentItem="True">

Related

Binding Observable collection list to dynamically created ComboBox in Silverlight

I have a requirement where I need to bind a list of observable collections to the item source of dynamically created ComboBoxes.
The problem is we bind through xaml conventionally in ItemsSource property but now the control rows in the grid are being added dynamically so each ComboBox in a row reference to the same collection whereas I need to bind it to a separate collection in observable collection list each time a row in the grid is added.
This what I have tried so far, any guidance would be appreciated. Thanks.
public virtual List<ObservableCollection<ComboBoxEntity>> ListRewardRule { get; set; }
Xaml :
<itimControls:ComboBox Name="cboReward"
IsMandatory="True"
itimComponents:ComponentManager.ComponentId="TXT_GROUP_RULE"
MaxWidth="400"
MinWidth="150"
ItemsSource="{Binding ListRewardRule, ElementName=RDefinitionScreen}"
DisplayMemberPath="Name"
SelectedValuePath="Code"
Loaded="cboReward_Loaded"
SelectedValue="{Binding RewardRuleId, Mode=TwoWay}"
SelectionChanged="cboReward_SelectionChanged">
</itimControls:ComboBox>
.CS :
private void cboReward_Loaded(object sender, RoutedEventArgs e)
{
Itim.Framework.Silverlight.UI.Controls.ComboBox cboReward = ((Itim.Framework.Silverlight.UI.Controls.ComboBox)sender);
int row = (int)cboReward.GetValue(Grid.RowProperty);
if (Model.ListRewardRule.Count > 0)
{
var rewardGroups = Model.RewardGroupAndTier.RewardGroups;
if(rewardGroups.Count > 1)
{
cboReward.ItemsSource = Model.ListRewardRule[row];
}
}
}
The way I understand this is, you have a “grid” (I suppose you mean a DataGrid) and each row in the grid has, among other things, a set of combo boxes.
What I’d do is bind that grid to an ObservableCollection of a custom class, call it CustomRowClass. CustomRowClass should have an ObservableCollection that will be bound to the combo boxes.
The magic is to define a DataTemplate for CustomRowClass. Once you set up the XAML this way, all you need to do is create an instance of CustomRowClass, and then add it to the ItemsSource of the grid.

Creating an autocomplete textbox with dropdown

My Problem:
I have a list of 118 chemical element names. And I wish to make a textbox in which as I type it will throw a dropdown menu with suggestion of names. I made this textbox in winforms and it was piece of cake however my efforts of making it in wpf are in vain. I've tried extended wpf toolkit, nimgoble and some other autocomplete textbox libs. So far dead end...I'm also new to wpf so maybe I'm missing something with these libs? I can't make them list my items and some won't even show the dropdown menu.
Heres what I wanted:
Here's how I finally achieved it:
So I solved this by using a combination of textbox and listbox. Where in textbox user types and as it changes (textbox changed event) it's checking for matches inside a list that holds names of all 118 elements and displays matches for the text typed in, inside listbox. Here's the code:
private void textBox_TextChanged(object sender, TextChangedEventArgs e)
{
listBox.Items.Clear();
if (textBox.Text.Trim() != "")
{
string regexPattern = (textBox.Text.ToString()) + "\\w*";
regexPattern = char.ToUpper(regexPattern[0]) + regexPattern.Substring(1); //prvo slovo veliko
Match match = Regex.Match(ElementNames.allElements, regexPattern);
while (match.Success && match.Value != "")
{
listBox.Items.Add(match.Value.ToString());
listBox.Visibility = Visibility.Visible;
match = match.NextMatch();
}
}
if (listBox.Items.IsEmpty || listBox.Items.Count == 119)
{
listBox.Visibility = Visibility.Collapsed;
if (listBox.Items.Count == 119) listBox.Items.Clear();
}
HighlightElementsOnTable();
OtherButtonsHighlight();
BringBackColors();
}
You can use a ComboBox with IsEditable=true.
Use a Combobox with
IsEditable = true
SelectedItem and Text data bound to the same property
StaysOpenOnEdit = true
<ComboBox ItemsSource="{Binding Models}" SelectedItem="{Binding Model}"
IsEditable="True" StaysOpenOnEdit="True" Text="{Binding Model, UpdateSourceTrigger=PropertyChanged}"/>

Dynamically fill the ComboBox of an DataGridComboBoxColumn (WPF, DataGrid)

in my WPF application, I have a DataGrid, which is bound to an ObservableCollection.
<DataGrid x:Name="DataGridTeilnehmer" HorizontalAlignment="Left" VerticalAlignment="Top" CellEditEnding="DataGridTeilnehmer_CellEditEnding" AutoGenerateColumns="False" SelectionMode="Single">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Teilnehmer" CellEditingTemplate="{StaticResource TeilnehmerEditTemplate}" CellTemplate="{StaticResource TeilnehmerCellTemplate}" />
<DataGridComboBoxColumn Header="Pass" />
...
The DataGridComboBoxColumn shall be filled with individual values for each row. The values depend on the entry of the first column. So, I would like to set the data in the CellEditEnding event like this:
private void DataGridTeilnehmer_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
if (!commiting)
{
commiting = true;
DataGridTeilnehmer.CommitEdit(DataGridEditingUnit.Row, false);
commiting = false;
// check, whether it is the first column that has been edited
if (...)
// get the list<string> for the combobox depending on the edited content
// get the combobox of the current row and bind the calculated list<string> to it
}
}
}
How can I do this?
EDIT: An example of what I am trying to achieve.
I have list of customers, which have individual tickets each. When the customer has been chosen in the first column, I want to load the ticket-list this customer has and load it into the next column - the combobox column.
Thanks in advance,
Frank
If you bound your datagrid to an ObservableCollection and your object implements INotifyPropertyChanged you can achieve what you need without using cell editending event.
In your model just check the value of your first column then set others columns values:
private string _firstColumn;
public string FirstColumn
{
get { return _firstColumn; }
set {
_firstColumn = value;
if(value == ...)
//set other properties
...
//notify the change
OnPropertyChanged("FirstColumn"); }
}
when your datagridrow lost focus all the new values will be notified to the datagrid

Silverlight - use combobox to select datagrid item

I'm a bit new to WPF and .NET, so this may be a simple task:
I have textblocks that get their values from the selected item of a datagrid. I'm trying to bind a combobox to one column in that datagrid so the user sees all the values in that datagrid column. When selecting an item in the combobox, it should make that row the selected item in the datagrid as well.
Here is my DataGrid:
<sdk:DataGrid AutoGenerateColumns="True" Name="l1dGrid" IsReadOnly="True" ItemsSource="{Binding}" DataContext="{Binding Path=DataContext}" />
and here is where the Datagrid gets data loaded:
_PCContext.Load(_PCContext.GetLine1_DownstairsQuery());
l1dGrid.ItemsSource = _PCContext.Line1_Downstairs;
Now I just need a combobox to have the ability to change the selected item in the DataGrid.Thanks in advance for any assistance!!!
[EDIT - SOLVED]
Ok, so I ended up just querying the datagrid based on the selected item of the combobox and setting the datagrid selected item to the one that matched that query.Here is the code I used to do this:
private void stockPick_comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string selection = stockPick_comboBox.SelectedValue.ToString().Replace("Line1_Downstairs : ", "");
selectGridItem(selection);
}
private void selectGridItem(string selection)
{
var stock = (from i in _PCContext.Line1_Downstairs
where i.Stock == selection
select i).FirstOrDefault();
l1dGrid.SelectedItem = stock;
}

How to make sure my WPF TabControl always has a selected tab when it contains at least one tab?

I have a TabControl whose items are bound to an ObservableCollection:
<TabControl ItemsSource="{Binding MyObservableCollection}" />
The tabs are added and removed as expected as items are added and removed from the collection. However, the SelectedItem reverts to -1 (meaning there is no selected tab) whenever the collection is empty. Then, when an item is added, the SelectedItem stays at -1 and the new tab is not selected.
How do I make the TabControl select the new tab whenever an item is added to the empty collection?
There might be an easier way, but you could hook the collection changed event on the ObservableCollection in your VM and set the SelectedItem property to the new item (assuming you have the selected item bound to a property on the VM).
What you can do is to subscribe for TabControl.ItemContainerGenerator.StatusChanged event and if the status is ContainersGenerated and the SelectedIndex of TabControl is -1, then to make the SelectedIndex of TabControl 0;
// peopleCollection is an ObservableCollection<Person>
People peopleCollection = new People();
public Window1()
{
InitializeComponent();
// MyTabControl is an instance of TabControl
MyTabControl.ItemsSource = peopleCollection;
MyTabControl.ItemContainerGenerator.StatusChanged += new EventHandler(ItemContainerGenerator_StatusChanged);
}
void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
if((sender as ItemContainerGenerator).Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated && MyTabControl.SelectedIndex == -1)
{
MyTabControl.SelectedIndex = 0;
}
}
There are 3-rd party solutions that has this functionality out of the box. Telerik's RadTabControl selects the first item whenever the collection changes its state from empty to "containing single item".
Try the demo here: http://demos.telerik.com/silverlight/#TabControl/AddingAndRemovingTabs
Note: It is a SL demo, but it works the same in WPF.
If you are looking for a pure MVVM implementation then, add a Index property to the ViewModel and on the CollectionChanged you can set Index=0 if there is no items inside. And in the XAML you can bind that Index as below
<TabControl ItemsSource="{Binding MyObservableCollection}" SelectedIndex="{Binding Index}" />
you're best bet is to probably overwrite the "OnTabAdded" functionality to check if a new one (first one) is added and then setting the SelectedItemIndex to 0;
since you are using ObservableCollection, you know when your collection changes, so I'd subscribe to the changed event form the collection and check the number of items in it.
I had the same problem and managed to fix it by binding the selected item to the first item in the dynamic list.
<TabControl ItemsSource="{Binding MyObservableCollection}" SelectedItem="{Binding MyObservableCollection.First}" />
Worked for me :)
<TabControl ItemsSource="{Binding MyObservableCollection}" SelectedItem="{Binding MyObservableCollection[0]}" />

Resources