Рассмотрите следующий XAML:
<ComboBox Name="CompanyComboBox"
HorizontalAlignment="Stretch"
ItemsSource="{Binding Path=GlobalData.Companies}"
SelectedValuePath="Id"
SelectedValue="{Binding Customer.CompanyId, ValidatesOnDataErrors=True}"
DisplayMemberPath="Name" />
GlobalData.Companies
набор (IEnumerable<Company>
) из компаний; этот набор может быть перезагружен на фоне (он загружается с веб-сервиса). Когда это происходит, ComboBox правильно перезагружает объекты через привязку. Однако как побочный эффект, это также сбрасывает выбранный пункт!
Я использовал Отражатель для осмотра источников поля комбинированного списка, и по-видимому это - предназначенное поведение.
Есть ли какой-либо "хороший" путь, как обойти это? То, чего я хочу достигнуть, то, что, если пользователь выбирает "Компанию" и перезагружает список компаний впоследствии, то "Компания" остается выбранной (принятие его находится в новом списке).
Для этой цели в 1,0 существовал With.ConstructorArgument
. В 2.0 синтаксис немного изменился: -
With.Parameters.ConstrityArgument с ninject 2,0
См. раздел Ввод значения в введенную зависимость для получения более подробной информации и примеров использования контекста, провайдеров и аргументов для более корректной передачи подобных данных.
EDIT: Поскольку Стивен решил сделать вид, что мой комментарий не имеет значения, я бы лучше прояснить, что я говорю на некоторых примерах (для 2,0):
MyClass m = kernel.Get<MyClass>( new ConstructorArgument( "i", 2) );
что для моих глаз очень ясно и точно указывает, что происходит.
Если вы находитесь в положении, где вы можете определить параметр более глобальным образом, вы можете зарегистрировать провайдера и сделать это так:
class MyClassProvider : SimpleProvider<MyClass>
{
protected override MyClass CreateInstance( IContext context )
{
return new MyClass( context.Kernel.Get<IService>(), CalculateINow() );
}
}
И зарегистрировать его так:
x => x.Bind<MyClass>().ToProvider( new MyClassProvider() )
NB CalculateINow ()
бит - это то, куда вы бы положили логику, как в первом ответе.
Или сделать его более сложным, как это:
class MyClassProviderCustom : SimpleProvider<MyClass>
{
readonly Func<int> _calculateINow;
public MyClassProviderCustom( Func<int> calculateINow )
{
_calculateINow = calculateINow;
}
protected override MyClass CreateInstance( IContext context )
{
return new MyClass( context.Kernel.Get<IService>(), _calculateINow() );
}
}
Какие вы бы зарегистрировали так:
x => x.Bind<MyClass>().ToProvider( new MyClassProviderCustom( ( ) => new Random( ).Next( 9 ) ) )
UPDATE: Новые механизмы, которые демонстрируют гораздо улучшенные модели с меньшим шаблоном, чем выше, воплощены в расширении Ninject.Extensions.Factory
, см.:
https://github.com/ninject/ninject.extensions.factory/wiki
Как указывалось ранее, если каждый раз нужно передавать разные параметры и в графике зависимостей имеется несколько уровней, может потребоваться выполнить что-то подобное .
Последнее соображение состоит в том, что, поскольку вы не указали Using < Behavior >
, по умолчанию будет установлено значение по умолчанию, как указано/по умолчанию в параметрах ядра ( Transient Behavior
в образце), что может привести к тому, что фабрика вычислит i
на лету, [например, если объект кэшируется]
Теперь, чтобы прояснить некоторые другие моменты в комментариях, которые FUDed и блеск над. Некоторые важные вещи, чтобы рассмотреть об использовании DI, будь то Ninject или что-то еще:
Есть как можно больше сделано конструктором инъекции, так что вам не нужно использовать контейнер конкретных атрибутов и трюков. Есть хорошая запись в блоге, которая называется Ваш IoC контейнер показывает .
Минимизируйте код, идущий к контейнеру и запрашивающий вещи - в противном случае ваш код связан с а) определенным контейнером (который CSL может минимизировать) b) способом, которым весь ваш проект выложен. Есть хорошие записи в блоге, которые показывают, что CSL не делает то, что вы думаете, что он делает. Этот общий раздел называется Расположение обслуживания по сравнению с вводом зависимостей . ОБНОВЛЕНИЕ: Подробное и полное обоснование см. в http://blog.ploeh.dk/2011/07/28/CompositionRoot.aspx .
Минимизируйте использование статики и одиночки
Не предполагайте, что существует только один [глобальный] контейнер, и что это нормально, чтобы просто требовать его, когда вам это нужно, как хорошая глобальная переменная. Правильное использование нескольких модулей и Bind.ToProvider ()
предоставляет структуру для управления этим.Таким образом, каждая отдельная подсистема может работать сама по себе, и у вас не будет низкоуровневых компонентов, связанных с компонентами верхнего уровня, и т.д.
Если кто-то хочет заполнить ссылки на блоги, на которые я имею в виду, я был бы признателен, что (все они уже связаны с другими постами на SO, так что все это просто дублирование пользовательского интерфейса, введенное с целью избежать путаницы вводящего в заблуждение ответа.)
Теперь, если бы только Джоэл мог войти и действительно настроить меня на то, что хороший синтаксис и/или правильный способ сделать это!
ОБНОВЛЕНИЕ: Хотя этот ответ явно полезен из числа полученных голосов, я хотел бы сделать следующие рекомендации:
В соответствии с документацией по перегрузке конструктора StackTrace, которая делает исключение
StackTrace создается с помощью текущий поток вызывающего абонента, но не содержать имя файла, номер строки или сведения о столбцах.
Чтобы получить номера строк , необходимо использовать перегрузку, которая принимает bool, а также исключение .
Для номеров строк также требуются файлы символов (pdb). Файлы символов доступны как для отладки, так и для сборки версий .
-121--3302923- Возможно, вместо IEnumerable < компания >
можно использовать ObservableCollection < компания >
? Затем при фоновом изменении следует только добавить/удалить предметы, которые являются новыми/отсутствуют в новом списке, выбранный предмет должен остаться, если он не был удален изменением.
Вы можете обновить наблюдаемую коллекцию в отдельном потоке с небольшим взломом .
Да, первый определенно лучше. Я бы никогда не пошел на второй метод.
Но вы должны думать об использовании полиморфизма больше. Использование instanceof
в таком тяжелом состоянии не является хорошим OO-дизайном.
Как сказал куби, пользуйтесь частным филиалом. Если вы вообще не хотите вставлять нарушенный код в репозиторий, вы можете отправить изменения по электронной почте как исправление (используйте git format-patch
), примените его дома и работайте оттуда (с git apply
)
Также помните, что вы можете использовать git commit --amend
, чтобы перезаписать «наполовину испеченную» фиксацию на правильную, считываемую как толкаемую к общему репо.
мм, я не знаю, является ли это «хорошим» способом, но если вы можете получить доступ к выбранному предмету до того, как произойдет перезагрузка, вы можете сохранить его (или его ключ или что-то в этом роде) и снова выбрать его программным способом после завершения перезагрузки.
Быстрое макетирование:
var selectedItem = myCombo.SelectedItem;
DoReload();
myCombo.SelectedItem = selectedItem;
Но я полагаю, вы имеете в виду другой способ, чем эта ручная работа вокруг?
Надеюсь, это поможет в любом случае...
UPDATE
OK Я вижу, из фонового потока.
Вы также используете IColllView для привязки combobox? Если это так, можно использовать свойство CurrentItem для сохранения ссылки. Я сделал быстрый макет, и это работает над моей установкой. предполагается, что имеется ссылка на пользовательский интерфейс:
XAML
<Grid VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<ComboBox ItemsSource="{Binding Items}" IsSynchronizedWithCurrentItem="True" Grid.Column="0" Grid.Row="0" DisplayMemberPath="Name"/>
<Button Command="{Binding UpdateCommand}" Grid.Column="1" Grid.Row="0">Update</Button>
</Grid>
View/ViewModel
public partial class Window1 : Window {
public Window1() {
InitializeComponent();
this.DataContext = new ViewModel(this);
}
}
public class ViewModel
{
private readonly Window1 window;
private ObservableCollection<Item> items;
private ICollectionView view;
public ViewModel(Window1 window) {
this.window = window;
items = new ObservableCollection<Item>
{
new Item("qwerty"),
new Item("hello"),
new Item("world"),
};
view = CollectionViewSource.GetDefaultView(items);
}
public ObservableCollection<Item> Items { get { return items; } }
public ICommand UpdateCommand {
get { return new RelayCommand(DoUpdate); }
}
public Item SelectedItem { get; set; }
private void DoUpdate(object obj) {
var act = new Func<List<Item>>(DoUpdateAsync);
act.BeginInvoke(CallBack, act);
}
private List<Item> DoUpdateAsync() {
return new List<Item> {
new Item("hello"),
new Item("world"),
new Item("qwerty"),
};
}
private void CallBack(IAsyncResult result) {
try {
var act = (Func<List<Item>>)result.AsyncState;
var list = act.EndInvoke(result);
window.Dispatcher.Invoke(new Action<List<Item>>(delegate(List<Item> lst) {
var current = lst.Single(i => i.Name == ((Item)view.CurrentItem).Name);
Items.Clear();
lst.ForEach(Items.Add);
view.MoveCurrentTo(current);
}), list);
} catch(Exception exc){ Debug.WriteLine(exc); }
}
}
public class Item {
public Item(string name) {
Name = name;
}
public string Name { get; set; }
}
В случае отсутствия выбранного предмета в списке потребуется выполнить определенную обработку.
Здесь важно свойство IsSynchronesingWireCurrentItem , иначе оно не будет работать!
Кроме того, путь на главное окно должна осуществляться с помощью DI-фреймворка.