Home > Blockchain >  How do I generate a checkbox in the code behind and send it as a child to the border on the MainWind
How do I generate a checkbox in the code behind and send it as a child to the border on the MainWind

Time:04-02

This is my Error about STA thread This picture is my error about STA thread.

  • Project is waiting incoming data. Actually I don't want to just wait for the application. and I'm try with dispatcher or Task but I take this error.
  private async void Window_Loaded(object sender, RoutedEventArgs e)
        {
            ToggleStart();

            var stackPanel = await Task.Run(TaskCheckBoxZones).ConfigureAwait(false);
            ZonesBorder.Child = stackPanel;

        

         //await Dispatcher.BeginInvoke((Action)async delegate
         //{
           //  var findCheckBoxes = new FindCheckBoxes
            // {
               //  CheckBoxItemTab = CheckBoxPanel,
                // CheckBoxItemTab2 = CheckBoxPanel2,
                // Token = token,
            // };
            // await findCheckBoxes.LoadPaymentsMethods();
         //});



        }
        async Task<StackPanel> TaskCheckBoxZones()
        {
            var stackPanel = await new CreateCheckBoxZones()
            {
                Token = token
            }.LoadZonesData();

            return stackPanel;
        }

Above code block in Settings.xaml.cs

public class CreateCheckBoxZones
    {
        private List<OrderZones> OrderZonesList;
        public string Token = "";
        public CreateCheckBoxZones()
        {
            OrderZonesList = new List<OrderZones>();
            _innerStack = new StackPanel();
        }

        //public Border Border;
        private StackPanel _innerStack;
        public StackPanel InnerStack
        {
            get { return _innerStack; }
            set { _innerStack = value; }
        }

        public async Task<StackPanel> LoadZonesData()
        {
            OrderZonesList = await MapOrderZones();

            foreach (var zone in OrderZonesList)
            {
                CheckBox cb = new CheckBox();
                cb.Tag = zone.Id;
                cb.Content = zone.Name;
                cb.IsChecked = zone.Status == 100;
                cb.Click  = ZonesCheckBox_Click;
                _innerStack.Children.Add(cb);
            }
            return _innerStack;
        }
        private async Task<List<OrderZones>> MapOrderZones()
        {
            var zoneModels = await TakeZones();

            if (zoneModels[0]._id == null)
                throw new Exception("Zones is can not null.");

            foreach (var zone in zoneModels)
            {
                OrderZonesList.Add(new OrderZones(
                    zone._id,
                    zone.placement,
                    zone.name.tr,
                    zone.minBasketSize,
                    zone.status,
                    zone.restaurant));
            }

            return OrderZonesList;
        }
        public async Task<List<ZoneModel>> TakeZones()
        {
            try
            {
                string url = Constants.GET_ZONES;

                var response = await new WebClient<List<ZoneModel>>()
                {
                    Endpoint = url,
                    Token = Token,
                    Method = Method.GET
                }.SendToAPIAsync();

                if (response.ErrorException != null)
                {
                    MessageBox.Show("Bölgeleri alırken, bir hata aldın.", "Bölgeler", MessageBoxButton.OK, MessageBoxImage.Error);
                }
                return response.Data;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            return null;
        }

above code block is about my LoadCheckbox class this class getting some API information with endpoints i take data but. My application is freezing during while take Define checkbox on UI. i am searching some solve think.

CodePudding user response:

You should make use of a ListBox (instead of a StackPanel) and a DataTemplate to generate the items (including the CheckBox).

  1. NEVER wrap an async Method into a Task.Run! Since TaskCheckBoxZones is an async method, the Task.Run(TaskCheckBoxZones) is a no-no. TaskCheckBoxZones or async methods in general should be named with the 'Async' suffix e.g. TaskCheckBoxZonesAsync. This will help you to recognize asynchronous methods to handle them correctly.
  2. You are using ConfigureAwait(false) wrong. ConfigureAwait(false) is meant to avoid a context switch back to the captured SynchronizationContext by allowing the continuation to execute on a thread pool thread. Fore example, when a Task captures the current SynchronizationContext of the main thread, then configuring it using ConfigureAwait(false) will likely force the continuation i.e. the code after the await, to execute on a non UI thread.
    Using ConfigureAwait(false) in a UI context is very likely to throw InvalidOperationException to indicate an illegal cross-thread operation:
private void onl oaded(object sender, EventArgs e)
{
  /** UI thread context **/

  // Task captures the current SynchronizationContext which is the UI thread.  
  var stackPanel = await Task.Run(TaskCheckBoxZones) // Never wrap an async method into a Task.Run
    .ConfigureAwait(false); // ConfigureAwait(false) will force continuation on a non UI thread.

  /** Worker thread context (non UI) **/

  ZonesBorder.Child = stackPanel; // Will throw InvalidOperationException (cross-thread)

To fix this, you should generally never use ConfigureAwait(false) in the UI context. It is meant for non UI library code. On UI level or in an application level context you very likely want to return to the UI thread after the async operation has completed:

private void onl oaded(object sender, EventArgs e)
{
  /** UI thread context **/

  // Task captures the current SynchronizationContext which is the UI thread.
  // Since ConfigureAwait(true) is the default behavior, the Task will return to the UI thread.  
  var stackPanel = await TaskCheckBoxZonesAsync(); 

  /** UI thread context **/

  ZonesBorder.Child = stackPanel; // Access allowed
  1. Don't use Dispatcher.BeginInvoke. It's and ancient method, part of the pre async/await API era. Since .NET Framework 4.5 we use Dispatcher.InvokeAsync.
  2. Don't enqueue code that executes on the UI thread to the Dispatcher. Additionally, awaiting the Dispatcher in this scenario is absolutely redundant. Rule 1) also applies to the Dispatcher: avoid wrapping an async method into a Dispatcher call:

Your bad version

await Dispatcher.BeginInvoke((Action)async delegate
{
  var findCheckBoxes = new FindCheckBoxes
  {
    CheckBoxItemTab = CheckBoxPanel,
    CheckBoxItemTab2 = CheckBoxPanel2,
    Token = token,
  };
  await findCheckBoxes.LoadPaymentsMethods();
});

The correct version

var findCheckBoxes = new FindCheckBoxes
{
  CheckBoxItemTab = CheckBoxPanel,
  CheckBoxItemTab2 = CheckBoxPanel2,
  Token = token,
};
await findCheckBoxes.LoadPaymentsMethodsAsync();
  1. Don't create a background thread only to create a control on the Dispatcher. This is absolutely pointless and expensive. If instantiation of your control is expensive consider to use an async initialization routine. Generally calling a constructor on a background thread is a serious code smell and an indicator of a poorly written class. A constructor must return immediately. A constructor should never be long-running:

Your bad version

private asnyc void OnButton_Click(object sender, EventArgs e)
{
  await Task.Run(() => Dispatcher.InvokeAsync(new MyDialogWindow().ShowDialog));
}

The correct version

private asnyc void OnButton_Click(object sender, EventArgs e)
{
  var dialog = new MyDialogWindow();
  await dialog.InitializeAsync(); // Perform longrunning and CPU intensive or IO initialization in a non blocking manner
  dialog.ShowDialog();
}
  1. There is no reason to let a private method return a member (e.g., a property). Just make it return void.
  2. Never catch the base class Exception and never show exception messages to the user. If you can recover from a particular exception, then only catch this one. Let the application crash on any other exception.

Solution

MainWindow.xaml.cs

partial class MainWIndow : Window
{
  // Use Clear, Add and Remove to modify this collection during runtime.
  // If you need to replace the complete collection (not recommended), then make this property into a dependency property.
  public ObservableCollection<OrderZone> OrderZones { get; }

  public MainWindow()
  {
    InitializeComponent();

    this.ZoneModels = new ObservableCollection<OrderZone>();
    this.Loaded  = onl oaded;
  }

  private async void onl oaded(object sender, EventArgs e)
  {
    var zoneCreator = new OrderZoneCreator();
    IAsyncEnumerable<OrderZone> orderZones = zoneCreator.EnumerateOrderZonesAsync();
    await foreach (OrderZone orderZone in orderZones)
    {
      this.OrderZones.Add(orderZone);
    }
  }
}

ZoneModelCreator.cs

public class OrderZoneCreator
{
  public async IAsyncEnumerable<OrderZone> EnumerateOrderZonesAsync()
  {
    List<ZoneModel> zoneModels = await GetZonesAsync();
    foreach (var zone in zoneModels)
    {
      var newOrderZone = new OrderZone(
        zone._id,
        zone.placement,
        zone.name.tr,
        zone.minBasketSize,
        zone.status,
        zone.restaurant);

      yield return newOrderZone;
    }
  }

  private async Task<List<ZoneModel>> GetZonesAsync()
  {
    string url = Constants.GET_ZONES;
    var webClient = new WebClient<List<ZoneModel>>()
    {
      Endpoint = url,
      Token = Token,
      Method = Method.GET
    };
    var response = await webClient.SendToAPIAsync();
    if (response.ErrorException != null)
    {
      // This is an internal operation and shouldn't generate user messages 
      // as the user can't do anything to make this operation succeed.
      // Throw an exception if this error is not acceptable
      // or do nothing and return an empty collection.
      return new List<ZoneModel>();
    }

    return response.Data;
  }
}

StatusToBooleanConverter.cs

class StatusToBooleanConverter : IValueConverter
{
  public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    => value is OrderZone orderZone
      ? orderZone.Status == 100
      : Binding.DoNothing;
  }

  public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    => throw new NotSupportedException();
}

MainWindow.xaml

<Window>
  <Window.Resources>
    <local:StatusToBooleanConverter x:Key="StatusToBooleanConverter" />
  </Window.Resources>

  <ListBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=OrderZones}"
           BorderBrush="Black"
           BorderThickness=2>
    <ListBox.ItemTemplate>
      <DataTemplate DataType="{x:Type local:OrderZone}">
        <CheckBox Content="{Binding Name, Mode OneTime}"
                  Tag="{Binding Id, Mode OneTime}"
                  Click="ZonesCheckBox_Click"
                  IsChecked="{Binding Status, Converter={StaticResource StatusToBooleanConverter}, Mode OneTime}" />
      </DataTemplate>
    </ListBox.ItemTemplate>
  </ListBox>
</Window>
  • Related