Проблема в том, что для вашего второго ввода нет обработки исключений.
travel_score = 0
while True:
try:
travel_score = int(input("How many times per year do you travel? Please give an integer number"))
except ValueError:
# if an exception raised here it propagates
travel_score = int(input("This was not a valid input please try again"))
print ("User travels per year:", travel_score)
Лучший способ справиться с этим - это вернуть информативное сообщение пользователю, если его ввод неверен, и позволить циклу вернуться в начало и повторить запрос таким образом:
# there is no need to instantiate the travel_score variable
while True:
try:
travel_score = int(input("How many times per year do you travel? Please give an integer number"))
except ValueError:
print("This was not a valid input please try again")
else:
break # <-- if the user inputs a valid score, this will break the input loop
print ("User travels per year:", travel_score)
Хорошо я нашел ответ (использующий много Отражателя, чтобы выяснить, как ComboBox работает).
Проблема существует, когда ItemSource установлен после того, как SelectedItem установлен. Когда это происходит, Combobx рассматривает его как полный Сброс выбора и очищает SelectedItem/SelectedIndex. Вы видите это здесь в Системе. Windows. Средства управления. Примитивы. Селектор (базовый класс для ComboBox):
protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
int selectedIndex = this.SelectedIndex;
bool flag = this.IsInit && this._initializingData.IsIndexSet;
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
if (!this.AddedWithSelectionSet(e.NewStartingIndex, e.NewStartingIndex + e.NewItems.Count))
{
if ((e.NewStartingIndex <= selectedIndex) && !flag)
{
this._processingSelectionPropertyChange = true;
this.SelectedIndex += e.NewItems.Count;
this._processingSelectionPropertyChange = false;
}
if (e.NewStartingIndex > this._focusedIndex)
{
return;
}
this.SetFocusedItem(this._focusedIndex + e.NewItems.Count, false);
}
return;
case NotifyCollectionChangedAction.Remove:
if (((e.OldStartingIndex > selectedIndex) || (selectedIndex >= (e.OldStartingIndex + e.OldItems.Count))) && (e.OldStartingIndex < selectedIndex))
{
this._processingSelectionPropertyChange = true;
this.SelectedIndex -= e.OldItems.Count;
this._processingSelectionPropertyChange = false;
}
if ((e.OldStartingIndex <= this._focusedIndex) && (this._focusedIndex < (e.OldStartingIndex + e.OldItems.Count)))
{
this.SetFocusedItem(-1, false);
return;
}
if (e.OldStartingIndex < selectedIndex)
{
this.SetFocusedItem(this._focusedIndex - e.OldItems.Count, false);
}
return;
case NotifyCollectionChangedAction.Replace:
if (!this.AddedWithSelectionSet(e.NewStartingIndex, e.NewStartingIndex + e.NewItems.Count))
{
if ((e.OldStartingIndex <= selectedIndex) && (selectedIndex < (e.OldStartingIndex + e.OldItems.Count)))
{
this.SelectedIndex = -1;
}
if ((e.OldStartingIndex > this._focusedIndex) || (this._focusedIndex >= (e.OldStartingIndex + e.OldItems.Count)))
{
return;
}
this.SetFocusedItem(-1, false);
}
return;
case NotifyCollectionChangedAction.Reset:
if (!this.AddedWithSelectionSet(0, base.Items.Count) && !flag)
{
this.SelectedIndex = -1;
this.SetFocusedItem(-1, false);
}
return;
}
throw new InvalidOperationException();
}
Отметьте последний случай - сброс... При загрузке нового ItemSource, Вы заканчиваете здесь, и какой-либо SelectedItem/SelectedIndex сдувается?!?!
Хорошо решение было довольно просто в конце. я просто разделил ошибочный ComboBox на подклассы и обеспечил и переопределение для этого метода следующим образом. Хотя я действительно должен был добавить a:
public class FixedComboBox : ComboBox
{
public FixedComboBox()
: base()
{
// This is here to sync the dep properties (OnSelectedItemChanged is private is the base class - thanks M$)
base.SelectionChanged += (s, e) => { FixedSelectedItem = SelectedItem; };
}
// need to add a safe dependency property here to bind to - this will store off the "requested selectedItem"
// this whole this is a kludgy wrapper because the OnSelectedItemChanged is private in the base class
public readonly static DependencyProperty FixedSelectedItemProperty = DependencyProperty.Register("FixedSelectedItem", typeof(object), typeof(FixedComboBox), new PropertyMetadata(null, new PropertyChangedCallback(FixedSelectedItemPropertyChanged)));
private static void FixedSelectedItemPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
FixedComboBox fcb = obj as FixedComboBox;
fcb.mLastSelection = e.NewValue;
fcb.SelectedItem = e.NewValue;
}
public object FixedSelectedItem
{
get { return GetValue(FixedSelectedItemProperty); }
set { SetValue(FixedSelectedItemProperty, value);}
}
protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
if (-1 == SelectedIndex)
{
// if after the base class is called, there is no selection, try
if (null != mLastSelection && Items.Contains(mLastSelection))
SelectedItem = mLastSelection;
}
}
protected object mLastSelection = null;
}
Все, что это делает, (a), сохраняют от старого SelectedItem, и затем (b) проверяют что, если после того, как ItemsChanged, если у нас нет сделанного выбора и старый SelectedItem, существует в новом списке... хорошо... Выбранный Это!
Не ясно из Вашего сообщения, знаете ли Вы, что необходимо изменить элементы UI на потоке UI - или у Вас будут проблемы. Вот краткий пример, который создает фоновый поток, который изменяет TextBox с текущим временем.
Ключом является MyTextBox. Dispather. BeginInvoke в Page.xaml.cs.
Page.xaml:
<UserControl x:Class="App.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300"
Loaded="UserControl_Loaded">
<Grid x:Name="LayoutRoot">
<TextBox FontSize="36" Text="Just getting started." x:Name="MyTextBox">
</TextBox>
</Grid>
</UserControl>
Page.xaml.cs:
using System;
using System.Windows;
using System.Windows.Controls;
namespace App
{
public partial class Page : UserControl
{
public Page()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
// Create our own thread because it runs forever.
new System.Threading.Thread(new System.Threading.ThreadStart(RunForever)).Start();
}
void RunForever()
{
System.Random rand = new Random();
while (true)
{
// We want to get the text on the background thread. The idea
// is to do as much work as possible on the background thread
// so that we do as little work as possible on the UI thread.
// Obviously this matters more for accessing a web service or
// database or doing complex computations - we do this to make
// the point.
var now = System.DateTime.Now;
string text = string.Format("{0}.{1}.{2}.{3}", now.Hour, now.Minute, now.Second, now.Millisecond);
// We must dispatch this work to the UI thread. If we try to
// set MyTextBox.Text from this background thread, an exception
// will be thrown.
MyTextBox.Dispatcher.BeginInvoke(delegate()
{
// This code is executed asynchronously on the
// Silverlight UI Thread.
MyTextBox.Text = text;
});
//
// This code is running on the background thread. If we executed this
// code on the UI thread, the UI would be unresponsive.
//
// Sleep between 0 and 2500 millisends.
System.Threading.Thread.Sleep(rand.Next(2500));
}
}
}
}
Так, если Вы хотите получить вещи асинхронно, необходимо будет использовать Управление. Диспетчер. BeginInvoke, чтобы уведомить элемент UI, что у Вас есть некоторые новые данные.
Вместо того, чтобы повторно связывать ItemsSource каждый раз, вам было бы проще привязать его к ObservableCollection <>, а затем вызвать Clear () для него и добавить (...) все элементы. Таким образом, привязка не сбрасывается.
Еще одна проблема заключается в том, что выбранный элемент ДОЛЖЕН быть экземпляром объектов в списке. Однажды я совершил ошибку, когда подумал, что список запросов для элемента по умолчанию был исправлен, но обновлялся при каждом вызове.
Я боролся с этой же проблемой, создавая каскадные поля со списком, и наткнулся на сообщение в блоге человека, который нашел простое, но удивительное решение. Вызовите UpdateLayout () после установки .ItemsSource, но перед установкой SelectedItem. Это должно заставить код блокироваться до завершения привязки данных. Я не совсем уверен, почему это исправляет, но я больше не сталкивался с состоянием гонки с тех пор ...
Источник этой информации: http://compiledexperience.com/Blog/post/Gotcha-when -databinding-a-ComboBox-in-Silverlight.aspx
Я был возмущен, когда впервые столкнулся с этой проблемой, но решил должен быть способ обойти это. Все мои усилия подробно описаны в этом посте.
http://blogs.msdn.com/b/kylemc/archive/2010/06/18/combobox-sample-for-ria-services.aspx
Я был очень доволен, поскольку это сузило синтаксис до чего-то вроде следующего.
<ComboBox Name="AComboBox"
ItemsSource="{Binding Data, ElementName=ASource}"
SelectedItem="{Binding A, Mode=TwoWay}"
ex:ComboBox.Mode="Async" />
Кайл