protected override void OnItemsSourceChanged(IEnumerable oldValue, IEnumerable newValue) { base.OnItemsSourceChanged(oldValue, newValue); //порядок вызова важен нужно сначала центрировать а потом ставить фокус if (SelectedItem != null) { //скорбная песнь, для того что бы вычислить вертикальное смещение //что бы отобразить строку по центру используется ViewportHeight //но при изменение ItemsSource меняется и ViewportHeight //но ViewportHeight будет пересчитан только когда будет обновлен layout //контрола (ArrangeOverride) //по этому мы говорим планировщику что нужно выполнить Centrify после того как он поделает все дела //это приводит к неприятному эффекту "дергания" когда таблица рисуется в одном положении //а затем почти мгновенно в другом ScrollIntoView(SelectedItem); Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => { DataGridHelper.Centrify(this); })); } //после обновление ItemsSource все визуальные элементы будут перестроены //и потеряют фокус //для того что бы восстановить фокус нужно запланировать это после того как новые элементы будут построены if (IsKeyboardFocusWithin) { Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => { DataGridHelper.Focus(this); })); } ItemSourceChanged?.Invoke(this, new EventArgs()); }
/* * проблемы с фокусом * 1 - тк ScrollViewer Focusable то он может получить фокус если на него кликнуть * это правильно когда таблица пуста для того что бы получать ввод с клавиатуры * и не правильно когда в таблице есть элементы * фокус должен оставаться на элементе */ public override void OnApplyTemplate() { base.OnApplyTemplate(); viewer = this.Descendants <ScrollViewer>().FirstOrDefault(s => s.Name == "DG_ScrollViewer"); if (viewer != null) { //тк viewer может получить фокус это будет происходить даже тогда когда не надо //например в случае если пользователь кликнет по пустому полю таблицы //фокус получит viewer и ввод с клавиатуры уйдет к нему не дав ни какого эффекта viewer.GotKeyboardFocus += (s, a) => { if (viewer.IsKeyboardFocused && Items.Count > 0) { DataGridHelper.Focus(this); } }; viewer.Focusable = true; } //ошибка в datagrid //при вычислении фактической ширины колонок datagrid пытается обределить ширину доступного поля //для этого используется scollviewer но если нет ни одной строки scrollviewer будет null //правим это if (viewer != null) { var host = GetProperty(this, "InternalScrollHost"); if (host == null) { SetField(this, "_internalScrollHost", viewer); } } }
private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { //если строка удаляет с помощью функции datagrid то после удалении фокус остается в datagrid //если же удаление производится из коллекции ItemsSource то CurrentItem сбрасывается в null //и таблица теряет фокус if (e.Action == NotifyCollectionChangedAction.Remove) { var index = Math.Min(e.OldStartingIndex, Items.Count - 1); if (index >= 0) { CurrentItem = Items[index]; SelectedItem = Items[index]; } } //если data grid получит событие Reset он убьет все ячейки и построит их заново //вместе с ячейками он убьет и фокус //Reset произойдет во множестве случаев в том числе если к данным применить сортировку Items.SortDescriptions.Add //что бы не терять фокус ловим событие и если владем фоксом восстанавливаем его после того как данные обновились if (e.Action == NotifyCollectionChangedAction.Reset && IsKeyboardFocusWithin) { Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => { DataGridHelper.Focus(this); })); } }
//основная идея: //видимость поля ввода привязана к поисковому запросу //если запрос есть то поле видимо //если поле видимо то мы подписываемся на события //изменения ячейки и - что бы знать когда нужно изменить //подписку на потерю фокуса //потери фокуса ячейки - что бы знать что человек ушел из таблицы //и поиск нужно сбросить //если поле стало скрытым все подписки надо освободить public static void AttachSearch(DataGrid grid, TextBox searchText) { grid.ObservableTextInput() .CatchSubscribe(e => RedirectInput(e, searchText)); var binding = BindingOperations.GetBinding(grid, Selector.SelectedItemProperty); //если биндинга нет это странно //магия что бы обойти ошибку в .net 4.0 см комментарий к CurrentItemStubProperty if (binding != null) { BindingOperations.SetBinding(grid, Controls.DataGrid2.CurrentItemStubProperty, new Binding { Path = new PropertyPath(binding.Path.Path, binding.Path.PathParameters), }); } grid.KeyDown += (sender, args) => { if (!searchText.IsEnabled) { return; } if (args.Key == Key.Escape) { if (!String.IsNullOrEmpty(searchText.Text)) { args.Handled = true; searchText.Text = null; } } }; var disposable = new CompositeDisposable(); searchText.IsVisibleChanged += (sender, args) => { if ((bool)args.NewValue) { var cellChangedSubscription = Observable .FromEventPattern <EventArgs>(grid, "CurrentCellChanged") .Subscribe(_ => AttachToCurrentCell(grid, disposable, searchText)); disposable.Add(cellChangedSubscription); if (!((bool)grid.GetValue(Selector.IsSelectionActiveProperty))) { DataGridHelper.Focus(grid); } AttachToCurrentCell(grid, disposable, searchText); } else { disposable.Dispose(); disposable = new CompositeDisposable(); } }; }
public CatalogSearchView() { InitializeComponent(); SearchText.KeyDown += (sender, args) => { if (args.Key == Key.Return) { DataGridHelper.Focus(Items); } }; ApplyStyles(); }
public void Focus_on_empty_data_grid() { WpfTestHelper.WithWindow2(async w => { var grid = new DataGrid2(); grid.AutoGenerateColumns = false; grid.Columns.Add(new DataGridTextColumn { Binding = new Binding("Items") }); w.Content = grid; grid.ItemsSource = Enumerable.Empty <Tuple <string> >().ToList(); await w.WaitIdle(); DataGridHelper.Focus(grid); Assert.IsTrue(grid.IsKeyboardFocusWithin); }); }
public Frontend2() { InitializeComponent(); Loaded += (sender, args) => { DataGridHelper.Focus(Lines); }; KeyDown += (sender, args) => { if (args.Key == Key.D && ((Keyboard.Modifiers & ModifierKeys.Control) != 0)) { Model.ShowDescription(); } if (args.Key == Key.F1) { Execute(Model.Help()); } if (args.Key == Key.F7) { Execute(Model.Close()); } if (args.Key == Key.F3) { Model.Clear(); } if (args.Key == Key.U && ((Keyboard.Modifiers & ModifierKeys.Control) != 0)) { Execute(Model.Unpack()); } if (args.Key == Key.F4) { Execute(Model.ReturnCheck()); } }; DataContextChanged += (sender, args) => { if (Model == null) { return; } var handler = new BarcodeHandler(this, Model.Settings); handler.Barcode.Subscribe(x => Execute(Model.BarcodeScanned(x))); }; new Editable().Attach(Lines); StyleHelper.ApplyStyles(typeof(CheckLine), Lines, Application.Current.Resources, Legend); }
protected override void OnActivate() { base.OnActivate(); if (!String.IsNullOrEmpty(CatalogNamesSearch.SearchText)) { CatalogNamesSearch.searchInProgress = true; Dispatcher.CurrentDispatcher.BeginInvoke( DispatcherPriority.Loaded, new System.Action(() => { var view = Views.Values.OfType <CatalogNameView>().FirstOrDefault(); var el = (DataGrid)view.FindName("CatalogNames"); DataGridHelper.Focus(el); CatalogNamesSearch.searchInProgress = false; })); } }
private void Loaded(object sender, RoutedEventArgs args) { //если внутри элемента управления есть своя логика восстановления фокуса //мы не должны перетирать ее //пример возврат из CatalogOfferViewModel при вводе с клавиатуры if (AssociatedObject.IsKeyboardFocusWithin) { return; } if (lastFocusedElement != null) { if (lastFocusedElement is DataGrid) { DataGridHelper.Focus((DataGrid)lastFocusedElement); } else { Keyboard.Focus(lastFocusedElement); } } else { var defaultFocus = GetDefaultFocus(AssociatedObject) ?? FromContent(AssociatedObject); if (defaultFocus == null) { return; } if (defaultFocus is DataGrid) { //иногда visual tree data grid оказывается не построенным хотя он и говорит что //все загружено, если фокус не удалось установить всего скорее visual tree не создан //нужно повторить операцию после того как все будет загружено if (!DataGridHelper.Focus((DataGrid)defaultFocus)) { Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() => { DataGridHelper.Focus((DataGrid)defaultFocus); })); } } else { Keyboard.Focus(defaultFocus); } } }
public void Do_not_reset_data_grid_focus() { WpfTestHelper.WithWindow2(async w => { var grid = new DataGrid2(); grid.AutoGenerateColumns = false; grid.Columns.Add(new DataGridTextColumn { Binding = new Binding("Items") }); w.Content = grid; grid.ItemsSource = Enumerable.Range(1, 100).Select(i => Tuple.Create(i.ToString())).ToList(); await grid.WaitLoaded(); DataGridHelper.Focus(grid); grid.ItemsSource = Enumerable.Range(500, 100).Select(i => Tuple.Create(i.ToString())).ToList(); await w.WaitIdle(); Assert.IsTrue(grid.IsKeyboardFocusWithin); Assert.IsInstanceOf <DataGridCell>(Keyboard.FocusedElement); }); }
public void Execute(ActionExecutionContext context) { var element = context.View.Descendants <FrameworkElement>().First(o => o.Name == name); if (element is DataGrid) { DataGridHelper.Focus((DataGrid)element); } else { element.Focus(); } if (Completed != null) { Completed(this, new ResultCompletionEventArgs()); } }
private static void KeyDown(object sender, KeyEventArgs args) { var el = (FrameworkElement)sender; if (args.Key == Key.Escape) { var prev = GetPrev(el); if (prev != null) { args.Handled = prev is DataGrid?DataGridHelper.Focus((DataGrid)prev) : prev.Focus(); } } if (args.Key == Key.Enter) { var next = GetNext(el); if (next != null) { args.Handled = next is DataGrid?DataGridHelper.Focus((DataGrid)next) : next.Focus(); } } }
public SearchOfferView() { InitializeComponent(); new Editable().Attach(Offers); DataGridHelper.CalculateColumnWidths(Offers); ApplyStyles(); BindingOperations.SetBinding(OfferOverlayPanel, Grid.MaxHeightProperty, new Binding("ActualHeight") { Source = Offers, Converter = new LambdaConverter <double>(v => v * 0.7) }); SearchText.KeyDown += (sender, args) => { if (args.Key == Key.Return) { DataGridHelper.Focus(Offers); } }; ProductInfo.ShowDescription.Content = "Описание (F1)"; }
public void Do_not_lose_focus_on_delete() { var items = Enumerable.Range(1, 100).Select(i => Tuple.Create(i.ToString())).ToList(); var data = new ObservableCollection <Tuple <String> >(items); WpfTestHelper.WithWindow2(async w => { var grid = new DataGrid2(); grid.AutoGenerateColumns = false; grid.Columns.Add(new DataGridTextColumn { Binding = new Binding("Items") }); w.Content = grid; grid.ItemsSource = data; await w.WaitIdle(); DataGridHelper.Focus(grid); Assert.IsNotNull(grid.CurrentItem); data.Remove((Tuple <string>)grid.SelectedItem); await w.WaitIdle(); Assert.IsTrue(grid.IsKeyboardFocusWithin); Assert.IsNotNull(grid.CurrentItem); }); }
public CatalogNameView() { InitializeComponent(); Promotions.SetBinding(MaxHeightProperty, new Binding("ActualHeight") { Mode = BindingMode.OneWay, Source = Catalogs, Converter = new LambdaConverter <double>(x => x / 2) }); SizeChanged += (sender, args) => { CatalogNamesColumn.MaxWidth = args.NewSize.Width / 2; }; Catalogs.KeyDown += (sender, args) => { if (args.Key == Key.Escape) { DataGridHelper.Focus(CatalogNames); args.Handled = true; } }; ApplyStyles(); }
private void Init() { if (model?.Waybill == null) { return; } //борьба за производительность //операции установки стиля приводят к перестроению дерева элементов wpf //что негативно отражается на производительности //что бы избежать ненужных перестроений дерева //сначала конструируем таблицу и настраиваем ее а затем добавляем в дерево элементов lines = new DataGrid2(); lines.Loaded += (sender, args) => { DataGridHelper.Focus(lines); }; lines.IsReadOnly = false; lines.Name = "Lines"; lines.Columns.Add(new DataGridTextColumnEx { Header = "Наименование", Binding = new Binding("Product"), Width = new DataGridLength(180, DataGridLengthUnitType.Star), SortDirection = ListSortDirection.Ascending, IsReadOnly = model.User.IsStockEnabled }); lines.Columns.Add(new DataGridTextColumnEx { Header = "Производитель", Binding = new Binding("Producer"), Width = new DataGridLength(180, DataGridLengthUnitType.Star), IsReadOnly = model.User.IsStockEnabled }); lines.Columns.Add(new DataGridTextColumnEx { Header = "Страна", Binding = new Binding("Country"), Width = new DataGridLength(100, DataGridLengthUnitType.Star), }); lines.Columns.Add(DataGridHelper.CheckBoxColumn("Печатать", "Print", x => lines.Items.OfType <WaybillLine>().Each(l => l.Print = x), true)); lines.Columns.Add(new DataGridTextColumnEx { Header = "Срок годности", Binding = new Binding("Period"), SortMemberPath = "Exp", Width = new DataGridLength(160, DataGridLengthUnitType.Star), }); lines.Columns.Add(new DataGridTextColumnEx { Header = "Серия товара", Binding = new Binding("SerialNumber"), Width = new DataGridLength(160, DataGridLengthUnitType.Star), }); lines.Columns.Add(new DataGridTextColumnEx { Width = new DataGridLength(13, DataGridLengthUnitType.Star), Header = "Штрихкод", Binding = new Binding("EAN13"), }); lines.Columns.Add(new CustomDataGridColumn((c, i) => null) { Header = "Сертификаты", Name = "CertificateLink", Width = new DataGridLength(1, DataGridLengthUnitType.SizeToHeader), Generator = (c, i) => new ContentControl { Style = (Style)FindResource("DownloadLink") } }); if (model.Waybill.IsCreatedByUser == true) { lines.CanUserAddRows = !model.User.IsStockEnabled; lines.CanUserDeleteRows = true; lines.Columns.Add(new CustomDataGridColumn { Header = "ЖНВЛС", Width = new DataGridLength(1, DataGridLengthUnitType.SizeToHeader), Generator = (c, i) => { var el = new CheckBox { VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center }; BindingOperations.SetBinding(el, CheckBox.IsCheckedProperty, new Binding("VitallyImportant") { UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged }); return(el); } }); } lines.Columns.Add(new DataGridTextColumnEx { Header = "Номер сертификата", Binding = new Binding("Certificates"), Width = new DataGridLength(180, DataGridLengthUnitType.Star), Visibility = Visibility.Collapsed }); lines.Columns.Add(new DataGridTextColumnEx { Header = "Цена производителя без НДС", Binding = new Binding("ProducerCost"), Width = new DataGridLength(1, DataGridLengthUnitType.Star), }); lines.Columns.Add(new DataGridTextColumnEx { Header = "Цена ГР", Binding = new Binding("RegistryCost"), Width = new DataGridLength(1, DataGridLengthUnitType.Star), }); lines.Columns.Add(new DataGridTextColumnEx { Header = "Торговая наценка оптовика", Binding = new Binding("SupplierPriceMarkup"), Width = new DataGridLength(1, DataGridLengthUnitType.Star), }); lines.Columns.Add(new DataGridTextColumnEx { Header = "Цена поставщика без НДС", Binding = new Binding("SupplierCostWithoutNds"), Width = new DataGridLength(1, DataGridLengthUnitType.Star), }); lines.Columns.Add(new DataGridTextColumnEx { Header = "НДС", Binding = new Binding("Nds"), Width = new DataGridLength(1, DataGridLengthUnitType.Star), }); lines.Columns.Add(new DataGridTextColumnEx { Header = "Цена поставщика с НДС", Binding = new Binding("SupplierCost") { Converter = InputConverter.Instance, ValidatesOnExceptions = true, }, Width = new DataGridLength(1, DataGridLengthUnitType.Star), IsReadOnly = false }); lines.Columns.Add(new DataGridTextColumnEx { Header = "Макс. розничная наценка", Binding = new Binding("MaxRetailMarkup"), Width = new DataGridLength(1, DataGridLengthUnitType.Star), IsReadOnly = true, }); lines.Columns.Add(new DataGridTextColumnEx { Header = "Розничная наценка", Binding = new Binding("RetailMarkup") { Converter = InputConverter.Instance, ValidatesOnExceptions = true, }, Width = new DataGridLength(1, DataGridLengthUnitType.Star), IsReadOnly = false, }); lines.Columns.Add(new DataGridTextColumnEx { Header = "Реальная наценка", Binding = new Binding("RealRetailMarkup") { Converter = InputConverter.Instance, ValidatesOnExceptions = true, }, Width = new DataGridLength(1, DataGridLengthUnitType.Star), IsReadOnly = false, }); lines.Columns.Add(new DataGridTextColumnEx { Header = "Розничная цена", Binding = new Binding("RetailCost") { Converter = InputConverter.Instance, ValidatesOnExceptions = true, }, Width = new DataGridLength(1, DataGridLengthUnitType.Star), IsReadOnly = false, }); lines.Columns.Add(new DataGridTextColumnEx { Header = "Заказ", Binding = new Binding("Quantity") { Converter = InputConverter.Instance, ValidatesOnExceptions = true, }, Width = new DataGridLength(1, DataGridLengthUnitType.Star), IsReadOnly = false }); lines.Columns.Add(new DataGridTextColumnEx { Header = "Розничная сумма", Binding = new Binding("RetailSum"), Width = new DataGridLength(1, DataGridLengthUnitType.Star), IsReadOnly = true, }); var grid = lines; DataGridHelper.CalculateColumnWidth(grid, "00000.00", "Цена производителя без НДС"); DataGridHelper.CalculateColumnWidth(grid, "00000.00", "Цена ГР"); DataGridHelper.CalculateColumnWidth(grid, "00000.00", "Торговая наценка оптовика"); DataGridHelper.CalculateColumnWidth(grid, "00000.00", "Цена поставщика без НДС"); DataGridHelper.CalculateColumnWidth(grid, "00000.00", "Цена поставщика с НДС"); DataGridHelper.CalculateColumnWidth(grid, "000", "НДС"); DataGridHelper.CalculateColumnWidth(grid, "00000.00", "Макс. розничная наценка"); DataGridHelper.CalculateColumnWidth(grid, "00000.00", "Розничная наценка"); DataGridHelper.CalculateColumnWidth(grid, "00000.00", "Реальная наценка"); DataGridHelper.CalculateColumnWidth(grid, "00000.00", "Розничная цена"); DataGridHelper.CalculateColumnWidth(grid, "00000.00", "Заказ"); DataGridHelper.CalculateColumnWidth(grid, "00000", "Оприходовано"); DataGridHelper.CalculateColumnWidth(grid, "00000.00", "Розничная сумма"); DataGridHelper.CalculateColumnWidth(grid, "0000000000000", "Штрихкод"); StyleHelper.ApplyStyles(typeof(WaybillLine), lines, Application.Current.Resources, Legend); Conventions.ConfigureDataGrid(lines, typeof(WaybillLine)); Grid.SetRow(lines, 2); Grid.SetColumn(lines, 1); Editable.AutoEditOnDigit(grid, "Розничная наценка"); if (model != null) { model.TableSettings.Restore(lines); model.TableSettings.Restore(OrderLines); foreach (var gridColumn in lines.Columns.OfType <DataGridTextColumn>().Where(c => !c.IsReadOnly)) { if (gridColumn.ReadLocalValue(DataGridColumn.IsReadOnlyProperty) == DependencyProperty.UnsetValue) { gridColumn.IsReadOnly = model.Waybill.IsReadOnly; } } model.Waybill.PropertyChanged += (sender, args) => { if (args.PropertyName == "IsCreatedByUser") { MainGrid.Children.Remove(lines); Init(); lines.ItemsSource = model.Lines.Value; } else if (args.PropertyName == "Status") { lines.CommitEdit(); //Завершаем редактирование, чтобы не исчезла текущая строка lines.IsReadOnly = model.Waybill.Status == DocStatus.Posted; } }; lines.IsReadOnly = model.Waybill.Status == DocStatus.Posted; } grid.BeginningEdit += (sender, args) => { var line = args.Row.DataContext as WaybillLine; if (line == null) { return; } if (line.ServerRetailCost != null) { MessageBox.Show(Application.Current.MainWindow, "Редактирование розничной цены запрещено поставщиком", Consts.WarningTitle, MessageBoxButton.OK, MessageBoxImage.Warning); args.Cancel = true; } else if (model.Waybill.Status == DocStatus.Posted) { MessageBox.Show(Application.Current.MainWindow, "Накладная оприходована, редактирование запрещено", Consts.WarningTitle, MessageBoxButton.OK, MessageBoxImage.Warning); args.Cancel = true; } }; MainGrid.Children.Add(lines); DataGridHelper.CalculateColumnWidth(OrderLines, "00000.00", "Цена"); DataGridHelper.CalculateColumnWidth(OrderLines, "00000.00", "Заказ"); DataGridHelper.CalculateColumnWidth(OrderLines, "00000.00", "Сумма"); }