Вот код, который я использую, и протестировал его на различных упомянутых версиях iOS. Очевидно, измените идентификатор клиента на свой:
- (void)showOurAppsInAppStore
{
NSString *searchUrl = nil;
// iPad
if ([DeviceController isDeviceAnIpad]) {
searchUrl = @"itms-apps://itunes.apple.com/us/artist/seligman-ventures-ltd/id326161338";
}
// iPhone / iPod Touch
else {
// iOS 7+
if ([DeviceController isDeviceOperatingSystemAtleast:@"7.0"]) {
searchUrl = @"itms-apps://itunes.apple.com/artist/seligman-ventures-ltd/id326161338";
}
// iOS 6
else if ([DeviceController isDeviceOperatingSystemAtleast:@"6.0"]) {
searchUrl = @"itms-apps://ax.itunes.apple.com/artist/seligman-ventures-ltd/id326161338";
}
// Pre iOS 6
else {
NSString *companyName = @"Seligman Ventures";
searchUrl = [NSString stringWithFormat:@"http://phobos.apple.com/WebObjects/MZSearch.woa/wa/search?WOURLEncoding=ISO8859_1&lang=1&output=lm&country=US&term=%@&media=software", [companyName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
}
}
[[UIApplication sharedApplication] openURL: [NSURL URLWithString:searchUrl]];
}
В стороне - чтобы избежать необходимости создавать бесчисленные типы делегатов:
if (panel.InvokeRequired)
{
panel.Invoke((MethodInvoker) delegate { AddControlToPanel(panel,ctrl); } );
return;
}
Кроме того, теперь выполняется регулярная статическая проверка внутреннего вызова AddControlToPanel
, так что вы не пойму неправильно.
Где создается pnlFoo
и в каком потоке? Вы знаете, когда создается его дескриптор? Проблема в том, что он создается в исходном (не UI) потоке
. Все управляющие дескрипторы в одном окне должны быть созданы и доступны в одном потоке. На этом этапе вам не потребуется две проверки, требуется ли Invoke, потому что ctrl
и панель
должны использовать один и тот же поток.
Если это не помогает, предоставьте короткую, но полную программу, чтобы продемонстрировать проблему.
Вот рабочий фрагмент кода:
public delegate void AddControlToPanelDlg(Panel p, Control c);
private void AddControlToPanel(Panel p, Control c)
{
p.Controls.Add(c);
}
private void AddNewContol(object state)
{
object[] param = (object[])state;
Panel p = (Panel)param[0];
Control c = (Control)param[1]
if (p.InvokeRequired)
{
p.Invoke(new AddControlToPanelDlg(AddControlToPanel), p, c);
}
else
{
AddControlToPanel(p, c);
}
}
А вот как я это тестировал. У вас должна быть форма с 2 кнопками и одним flowLayoutPanel (я выбрал это, чтобы мне не приходилось заботиться о местоположении h при динамическом добавлении элементов управления на панели)
private void button1_Click(object sender, EventArgs e)
{
AddNewContol(new object[]{flowLayoutPanel1, CreateButton(DateTime.Now.Ticks.ToString())});
}
private void button2_Click(object sender, EventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(AddNewContol), new object[] { flowLayoutPanel1, CreateButton(DateTime.Now.Ticks.ToString()) });
}
Я выяснил, что когда вы входите ветвь InvokeRequired вызывается той же функцией, что и вы, что приводит к странному случаю рекурсии.
«панель» и «ctrl» должны быть созданы в одном потоке, т.е. у вас не может быть panel.InvokeRequired, возвращающего значение, отличное от ctrl.InvokeRequired. То есть , если и панель, и ctrl создали дескрипторы или принадлежат контейнеру с созданным дескриптором . Из MSDN :
Если дескриптор элемента управления еще не существует, InvokeRequired выполняет поиск по родительская цепочка элемента управления, пока не найдет элемент управления или форма, у которой есть оконная ручка. Если нет подходящего ручку можно найти, Метод InvokeRequired возвращает false.
На данный момент ваш код открыт для условий гонки , потому что панель .InvokeNeeded
может вернуть false, потому что панель еще не создана, тогда ctrl.InvokeNeeded
обязательно вернет false, потому что, скорее всего, ctrl еще не добавлен ни в один контейнер, а затем, когда вы достигнете panel.Controls.Add
, панель была создана в основном потоке, поэтому вызов не будет выполнен.
В вашем собственном ответе вы указываете:
Чтобы прояснить ситуацию: «панель» создается в базовом потоке, а «ctrl» - в новом потоке
Я думаю, что это может быть причина вашей проблемы. Все элементы UI должны быть созданы в одном потоке (базовом). Если вам нужно создать «ctrl» как следствие какого-либо действия в новом потоке, затем вызовите событие обратно в базовый поток и выполните создание там.
Вот небольшой фрагмент полной программы:
public class ucFoo : UserControl
{
private Panel pnlFoo = new Panel();
public ucFoo()
{
this.Controls.Add(pnlFoo);
}
}
public class ucFoo2 : UserControl
{
private Panel pnlFooContainer = new Panel();
public ucFoo2()
{
this.Controls.Add(pnlFooContainer);
Thread t = new Thread(new ThreadStart(AddFooControlToFooConatiner());
t.Start()
}
private AddFooControlToFooConatiner()
{
ucFoo foo = new ucFoo();
this.pnlFooContainer.Controls.Add(ucFoo); //<-- this is where the exception is raised
}
}
Здесь много интересных ответов, но одним из ключевых элементов для любой многопоточности в приложении Winform является использование BackgroundWorker для инициации потоков и обратной связи с основным потоком Winform.
.