MaterialDesignInXAML/MaterialDesignInXamlToolkit · error · ArgumentException

SnackbarMessageQueue must be created by the same thread.

Error message

SnackbarMessageQueue must be created by the same thread.

What it means

The Snackbar.MessageQueue dependency property registers a validate-value callback that ensures the SnackbarMessageQueue shares the Snackbar's Dispatcher. WPF dependency objects are dispatcher-affine; pairing a queue from another thread would cause cross-thread access exceptions later, so the callback throws ArgumentException eagerly at assignment time.

Source

Thrown at src/MaterialDesignThemes.Wpf/Snackbar.cs:55

    }

    public static readonly DependencyProperty MessageQueueProperty = DependencyProperty.Register(
        nameof(MessageQueue), typeof(SnackbarMessageQueue), typeof(Snackbar), new PropertyMetadata(default(SnackbarMessageQueue), MessageQueuePropertyChangedCallback),
        MessageQueueValidateValueCallback);

    private static void MessageQueuePropertyChangedCallback(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
    {
        var snackbar = (Snackbar)dependencyObject;
        snackbar._messageQueueRegistrationCleanUp?.Invoke();
        var messageQueue = dependencyPropertyChangedEventArgs.NewValue as SnackbarMessageQueue;
        snackbar._messageQueueRegistrationCleanUp = messageQueue?.Pair(snackbar);
    }

    private static bool MessageQueueValidateValueCallback(object value)
    {
        if (value is null || ((SnackbarMessageQueue)value).Dispatcher == Dispatcher.CurrentDispatcher)
            return true;
        throw new ArgumentException("SnackbarMessageQueue must be created by the same thread.", nameof(value));
    }

    public SnackbarMessageQueue? MessageQueue
    {
        get => (SnackbarMessageQueue?)GetValue(MessageQueueProperty);
        set => SetValue(MessageQueueProperty, value);
    }

    public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register(
        nameof(IsActive), typeof(bool), typeof(Snackbar), new PropertyMetadata(default(bool), IsActivePropertyChangedCallback));

    public bool IsActive
    {
        get => (bool)GetValue(IsActiveProperty);
        set => SetValue(IsActiveProperty, value);
    }

    public event RoutedPropertyChangedEventHandler<bool> IsActiveChanged

View on GitHub (pinned to 98edec3a0b)

Solutions

  1. Construct the SnackbarMessageQueue on the UI (dispatcher) thread, e.g. in the view's constructor.
  2. Pass an explicit Dispatcher into the queue constructor: new SnackbarMessageQueue(duration, Application.Current.Dispatcher).
  3. Bind Snackbar.MessageQueue to a queue owned by the same dispatcher as the control, or create the queue lazily on the UI thread.

Example fix

// before (queue built on a Task thread)
var queue = await Task.Run(() => new SnackbarMessageQueue());
snackbar.MessageQueue = queue;
// after
var queue = new SnackbarMessageQueue(TimeSpan.FromSeconds(3), Application.Current.Dispatcher);
snackbar.MessageQueue = queue;
Defensive patterns

Strategy: validation

Validate before calling

if (queue.Dispatcher != snackbar.Dispatcher)
    throw new InvalidOperationException("Queue must share the Snackbar's dispatcher.");
snackbar.MessageQueue = queue;

Prevention

When it happens

Trigger: Assigning a SnackbarMessageQueue created on a different thread than the Snackbar control, e.g. `snackbar.MessageQueue = queueFromAnotherThread;` or binding to a queue resolved on a background dispatcher.

Common situations: A DI container resolving the queue on a non-UI thread; a Task.Run factory constructing the queue; multi-window setups with separate dispatchers; a ViewModel owned by a worker thread.

Related errors


AI-assisted analysis of MaterialDesignInXAML/MaterialDesignInXamlToolkit@98edec3a0b (2026-08-13). Data as JSON: /api/errors/6cab7bd615901bc2. Report an issue: GitHub.