Skip to main content

ferroui_controls/
control.rs

1use crate::automation::peers::{AutomationPeer, NoneAutomationPeer};
2use crate::primitives::{AdornerLayer, FlyoutBase};
3use crate::templates::{DataTemplates, IDataTemplateHost, ITemplateOf};
4use crate::Panel;
5use crate::ContextMenu;
6use ferroui_base::data::core::ValueTypes;
7use ferroui_base::reactive::IDisposable;
8use crate::{RequestBringIntoViewEventArgs, SizeChangedEventArgs};
9use ferroui_base::input::{
10    ContextRequestedEventArgs, FocusChangedEventArgs, InputElement, InputElementImpl, InputElementImplExt, KeyEventArgs,
11    MouseButton, NavigationMethod,
12    PointerReleasedEventArgs,
13};
14use ferroui_base::interactivity::{
15    Interactive, InteractiveImpl, RoutedEvent, RoutedEventArgs, RoutedEventHandlerToken, RoutingStrategies,
16};
17use ferroui_base::layout::LayoutableImpl;
18use ferroui_base::styling::{ISetterValue, Setter};
19use ferroui_base::threading::{Dispatcher, DispatcherPriority};
20use ferroui_base::{
21    ferro_class, ferro_impl_classes, ferro_property, ferro_routed_event, instantiate, AnyValue, BoxedValue,
22    FerroObjectImpl, FerroObjectImplExt, FerroProperty, FerroPropertyChangedEventArgs, IntoRef, Nullable, Rect,
23    Ref, Size, StyledElement, StyledElementImpl, StyledProperty, Visual, VisualImpl, VisualImplExt, VisualTreeAttachmentEventArgs,
24};
25use std::cell::{Cell, OnceCell, RefCell};
26use std::collections::{HashSet, VecDeque};
27use std::rc::Rc;
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30enum LoadState {
31    Unloaded,
32    Loaded,
33    LoadPending,
34}
35
36// Note the following:
37// `queue`:
38//   Is the queue where any control will be added to indicate that its loaded
39//   event should be scheduled and called later.
40// `processing_queue`:
41//   Contains a copied snapshot of `queue` at the time when processing starts
42//   and individual events are being fired. This is needed because new
43//   controls can be added in the Loaded event itself.
44#[derive(Default)]
45struct LoadedQueues {
46    is_loaded_processing: bool,
47    /// The queued controls in insertion order.
48    queue: Vec<Ref<Control>>,
49    /// The members of `queue`, for constant time lookups.
50    queued: HashSet<Ref<Control>>,
51    processing_queue: VecDeque<Ref<Control>>,
52}
53
54impl LoadedQueues {
55    fn add(&mut self, control: Ref<Control>) -> bool {
56        if self.queued.insert(control.clone()) {
57            self.queue.push(control);
58            true
59        } else {
60            false
61        }
62    }
63
64    fn remove(&mut self, control: &Ref<Control>) {
65        if self.queued.remove(control) {
66            self.queue.retain(|c| c != control);
67        }
68    }
69}
70
71ferroui_base::ferro_thread_local! {
72    static LOADED: RefCell<LoadedQueues> = RefCell::new(LoadedQueues::default());
73}
74
75/// Base class for controls.
76///
77/// The control class extends [`InputElement`] and adds the following
78/// features:
79///
80/// - A `Tag` property to allow user-defined data to be attached to the
81///   control.
82#[repr(C)]
83pub struct Control {
84    base: InputElement,
85    load_state: Cell<LoadState>,
86    data_templates: OnceCell<DataTemplates>,
87    focus_adorner: RefCell<Option<Ref<Control>>>,
88    context_flyout_handlers: Cell<Option<(RoutedEventHandlerToken, RoutedEventHandlerToken)>>,
89    context_menu_handlers: Cell<Option<ContextMenuHandlers>>,
90    automation_peer: RefCell<Option<Ref<AutomationPeer>>>,
91}
92
93/// The handlers the context menu support attaches to a control: the context
94/// requested and context canceled handlers and the subscriptions to the
95/// attachment to and the detachment from the visual tree.
96pub(crate) type ContextMenuHandlers =
97    (RoutedEventHandlerToken, RoutedEventHandlerToken, Rc<dyn IDisposable>, Rc<dyn IDisposable>);
98
99ferro_class! {
100    Control: InputElement, virtuals ControlImpl: InputElementImpl {
101        /// Gets the element that receives the focus adorner.
102        fn get_template_focus_target(this) -> Option<Ref<Control>>;
103        /// Raises the `Loaded` event.
104        fn on_loaded(this, e: &RoutedEventArgs);
105        /// Raises the `Unloaded` event.
106        fn on_unloaded(this, e: &RoutedEventArgs);
107        /// Raises the `SizeChanged` event.
108        fn on_size_changed(this, e: &SizeChangedEventArgs);
109        /// Returns a new, type-specific automation peer implementation for
110        /// the control.
111        fn on_create_automation_peer(this) -> Ref<AutomationPeer>;
112    }
113}
114ferroui_base::ferro_class_info!(Control { new: Control::new });
115
116ferro_impl_classes!(Control: StyledElementImpl, LayoutableImpl, InteractiveImpl);
117
118ferroui_base::ferro_overrides! { impl FerroObjectImpl for Control {
119    fn on_property_changed(this: &Self, change: &FerroPropertyChangedEventArgs<'_>) {
120        Self::parent_on_property_changed(this, change);
121
122        if change.property() == Visual::bounds_property().as_property() {
123            let (old_value, new_value) = change.get_old_and_new_value::<Rect>();
124
125            // Bounds is a rect with a position as well as a height and width.
126            // This means it is possible for the rect to change position but
127            // not size. Therefore, we want to explicitly check only the size
128            // and raise an event only when that size has changed.
129            if new_value.size() != old_value.size() {
130                let size_changed_event_args = SizeChangedEventArgs::new(
131                    Self::size_changed_event(),
132                    this.to_ref(),
133                    Size::new(old_value.width, old_value.height),
134                    Size::new(new_value.width, new_value.height),
135                );
136
137                this.on_size_changed(&size_changed_event_args);
138            }
139        }
140    }
141
142    fn update_data_validation(
143        this: &Self,
144        property: &'static ferroui_base::FerroProperty,
145        state: ferroui_base::data::BindingValueType,
146        error: Option<&ferroui_base::data::BindingError>,
147    ) {
148        crate::DataValidationErrors::set_error(this, error);
149        Self::parent_update_data_validation(this, property, state, error);
150    }
151} }
152
153impl VisualImpl for Control {
154    fn on_attached_to_visual_tree_core(this: &Self, e: &VisualTreeAttachmentEventArgs) {
155        Self::parent_on_attached_to_visual_tree_core(this, e);
156
157        this.initialize_if_needed();
158
159        this.schedule_on_loaded_core();
160    }
161
162    fn on_detached_from_visual_tree_core(this: &Self, e: &VisualTreeAttachmentEventArgs) {
163        Self::parent_on_detached_from_visual_tree_core(this, e);
164
165        this.on_unloaded_core();
166    }
167
168    fn ensure_initialized_for_visual_brush(this: &Self) {
169        this.ensure_initialized();
170    }
171}
172
173impl InputElementImpl for Control {
174    fn on_got_focus(this: &Self, e: &FocusChangedEventArgs) {
175        Self::parent_on_got_focus(this, e);
176
177        if this.is_focused()
178            && (e.navigation_method == NavigationMethod::Tab || e.navigation_method == NavigationMethod::Directional)
179        {
180            if let Some(adorner_layer) = AdornerLayer::get_adorner_layer(this) {
181                if this.focus_adorner.borrow().is_none() {
182                    let template = if this.is_set(Self::focus_adorner_property().as_property()) {
183                        this.get_value(Self::focus_adorner_property())
184                    } else {
185                        adorner_layer.default_focus_adorner()
186                    };
187
188                    if let Some(template) = template {
189                        *this.focus_adorner.borrow_mut() = template.build_typed();
190                    }
191                }
192
193                let focus_adorner = this.focus_adorner.borrow().clone();
194                if let (Some(focus_adorner), Some(target)) = (focus_adorner, this.get_template_focus_target()) {
195                    AdornerLayer::set_adorned_element(&focus_adorner, target);
196                    adorner_layer.children().add(focus_adorner);
197                }
198            }
199        }
200    }
201
202    fn on_lost_focus(this: &Self, e: &FocusChangedEventArgs) {
203        Self::parent_on_lost_focus(this, e);
204
205        let focus_adorner = this.focus_adorner.borrow().clone();
206        if let Some(focus_adorner) = focus_adorner {
207            if let Some(adorner_layer) = focus_adorner.parent().and_then(|parent| parent.cast::<Panel>()) {
208                adorner_layer.children().remove(focus_adorner);
209                *this.focus_adorner.borrow_mut() = None;
210            }
211        }
212    }
213
214    fn on_pointer_released(this: &Self, e: &PointerReleasedEventArgs) {
215        Self::parent_on_pointer_released(this, e);
216
217        if e.is_source(this) && !e.handled() && e.initial_press_mouse_button() == MouseButton::Right {
218            let args = ContextRequestedEventArgs::from_pointer_event_args(e);
219            this.raise_event(&args);
220            e.set_handled(args.handled());
221        }
222    }
223
224    fn on_key_up(this: &Self, e: &KeyEventArgs) {
225        Self::parent_on_key_up(this, e);
226
227        if e.is_source(this) && !e.handled() {
228            let Some(settings) = this.get_platform_settings() else { return };
229            let configuration = settings.hotkey_configuration();
230            let keymap = &configuration.open_context_menu;
231
232            let matches = keymap.iter().any(|key| key.matches(Some(e)));
233
234            if matches {
235                let args = ContextRequestedEventArgs::new();
236                this.raise_event(&args);
237                e.set_handled(args.handled());
238            }
239        }
240    }
241}
242
243impl ControlImpl for Control {
244    fn get_template_focus_target(this: &Self) -> Option<Ref<Control>> {
245        Some(this.to_ref())
246    }
247
248    fn on_loaded(this: &Self, e: &RoutedEventArgs) {
249        this.raise_event(e);
250    }
251
252    fn on_unloaded(this: &Self, e: &RoutedEventArgs) {
253        this.raise_event(e);
254    }
255
256    fn on_create_automation_peer(this: &Self) -> Ref<AutomationPeer> {
257        NoneAutomationPeer::new(this).upcast()
258    }
259
260    fn on_size_changed(this: &Self, e: &SizeChangedEventArgs) {
261        this.raise_event(e);
262    }
263}
264
265ferroui_base::ferro_properties! { impl Control {
266    ferro_property!(
267        /// Defines the `FocusAdorner` property.
268        pub fn focus_adorner_property() -> StyledProperty<Option<Rc<dyn ITemplateOf<Option<Ref<Control>>>>>> {
269            FerroProperty::register::<Control, _>("FocusAdorner", None)
270        }
271    );
272
273    ferro_property!(
274        /// Defines the `Tag` property.
275        pub fn tag_property() -> StyledProperty<Option<BoxedValue>> {
276            FerroProperty::register::<Control, _>("Tag", None)
277        }
278    );
279
280    ferro_property!(
281        /// Defines the `ContextMenu` property.
282        pub fn context_menu_property() -> StyledProperty<Option<Ref<ContextMenu>>> {
283            FerroProperty::register::<Control, _>("ContextMenu", None)
284        }
285    );
286
287    ferro_property!(
288        /// Defines the `ContextFlyout` property.
289        pub fn context_flyout_property() -> StyledProperty<Option<Ref<FlyoutBase>>> {
290            FerroProperty::register::<Control, _>("ContextFlyout", None)
291        }
292    );
293} }
294
295impl Control {
296    ferro_routed_event!(
297        /// Event raised when an element wishes to be scrolled into view.
298        pub fn request_bring_into_view_event() -> RoutedEvent<RequestBringIntoViewEventArgs> {
299            RoutedEvent::register::<Control, _>("RequestBringIntoView", RoutingStrategies::BUBBLE)
300        }
301    );
302
303    ferro_routed_event!(
304        /// Defines the `Loaded` event.
305        pub fn loaded_event() -> RoutedEvent<RoutedEventArgs> {
306            RoutedEvent::register::<Control, _>("Loaded", RoutingStrategies::DIRECT)
307        }
308    );
309
310    ferro_routed_event!(
311        /// Defines the `Unloaded` event.
312        pub fn unloaded_event() -> RoutedEvent<RoutedEventArgs> {
313            RoutedEvent::register::<Control, _>("Unloaded", RoutingStrategies::DIRECT)
314        }
315    );
316
317    ferro_routed_event!(
318        /// Defines the `SizeChanged` event.
319        pub fn size_changed_event() -> RoutedEvent<SizeChangedEventArgs> {
320            RoutedEvent::register::<Control, _>("SizeChanged", RoutingStrategies::DIRECT)
321        }
322    );
323
324    /// Creates the class data; see [`ferroui_base::FerroObject::construct`].
325    pub fn construct() -> Self {
326        Self { base: InputElement::construct(), load_state: Cell::new(LoadState::Unloaded), data_templates: OnceCell::new(), focus_adorner: RefCell::new(None), context_flyout_handlers: Cell::new(None), context_menu_handlers: Cell::new(None), automation_peer: RefCell::new(None) }
327    }
328
329    pub fn new() -> Ref<Self> {
330        instantiate(Self::construct())
331    }
332
333    /// Gets the automation peer of the control, if it has been created.
334    pub(crate) fn get_automation_peer(&self) -> Option<Ref<AutomationPeer>> {
335        self.verify_access();
336        self.automation_peer.borrow().clone()
337    }
338
339    /// Gets the automation peer of the control, creating it with
340    /// `on_create_automation_peer` if it does not exist yet.
341    pub(crate) fn get_or_create_automation_peer(&self) -> Ref<AutomationPeer> {
342        self.verify_access();
343
344        if let Some(peer) = self.automation_peer.borrow().clone() {
345            return peer;
346        }
347
348        let peer = self.on_create_automation_peer();
349        *self.automation_peer.borrow_mut() = Some(peer.clone());
350        peer
351    }
352
353    /// The control's focus adorner.
354    pub fn focus_adorner(&self) -> Option<Rc<dyn ITemplateOf<Option<Ref<Control>>>>> {
355        self.get_value(Self::focus_adorner_property())
356    }
357
358    pub fn set_focus_adorner(&self, value: Option<Rc<dyn ITemplateOf<Option<Ref<Control>>>>>) {
359        self.set_value(Self::focus_adorner_property(), value)
360    }
361
362    /// The data templates for the control.
363    ///
364    /// Each control may define data templates which are applied to the
365    /// control itself and its children.
366    pub fn data_templates(&self) -> DataTemplates {
367        self.data_templates.get_or_init(DataTemplates::new).clone()
368    }
369
370    /// Whether the data templates collection has been created.
371    pub fn is_data_templates_initialized(&self) -> bool {
372        self.data_templates.get().is_some()
373    }
374
375    /// A context menu to the control.
376    pub fn context_menu(&self) -> Option<Ref<ContextMenu>> {
377        self.get_value(Self::context_menu_property())
378    }
379
380    pub fn set_context_menu(&self, value: impl Into<Nullable<ContextMenu>>) {
381        self.set_value(Self::context_menu_property(), value.into().0)
382    }
383
384    /// The handlers the context menu support attached to this control.
385    pub(crate) fn context_menu_handlers(&self) -> &Cell<Option<ContextMenuHandlers>> {
386        &self.context_menu_handlers
387    }
388
389    /// A context flyout to the control.
390    pub fn context_flyout(&self) -> Option<Ref<FlyoutBase>> {
391        self.get_value(Self::context_flyout_property())
392    }
393
394    pub fn set_context_flyout(&self, value: impl Into<Nullable<FlyoutBase>>) {
395        self.set_value(Self::context_flyout_property(), value.into().0)
396    }
397
398    /// The handlers the context flyout support attached to this control
399    /// for its context requested and context canceled events.
400    pub(crate) fn context_flyout_handlers(&self) -> &Cell<Option<(RoutedEventHandlerToken, RoutedEventHandlerToken)>> {
401        &self.context_flyout_handlers
402    }
403
404    /// Whether the control is fully constructed in the visual tree and both
405    /// layout and render are complete.
406    ///
407    /// This is set to true while raising the `Loaded` event.
408    pub fn is_loaded(&self) -> bool {
409        self.load_state.get() == LoadState::Loaded
410    }
411
412    /// A user-defined object attached to the control.
413    pub fn tag(&self) -> Option<BoxedValue> {
414        self.get_value(Self::tag_property())
415    }
416
417    pub fn set_tag(&self, value: Option<BoxedValue>) {
418        self.set_value(Self::tag_property(), value)
419    }
420
421    /// Occurs when the control has been fully constructed in the visual tree
422    /// and both layout and render are complete.
423    ///
424    /// This event is guaranteed to occur after the control template is
425    /// applied and references to objects created after the template is
426    /// applied are available. This makes it different from
427    /// `on_attached_to_visual_tree` which doesn't have these references. This
428    /// event occurs at the latest possible time in the control creation
429    /// life-cycle.
430    pub fn loaded(&self, handler: impl Fn(&Interactive, &RoutedEventArgs) + 'static) -> RoutedEventHandlerToken {
431        self.add_handler(Self::loaded_event(), handler)
432    }
433
434    /// Occurs when the control is removed from the visual tree.
435    ///
436    /// This is API symmetrical with `loaded` and exists for compatibility
437    /// with other XAML frameworks; however, it behaves the same as
438    /// `on_detached_from_visual_tree`.
439    pub fn unloaded(&self, handler: impl Fn(&Interactive, &RoutedEventArgs) + 'static) -> RoutedEventHandlerToken {
440        self.add_handler(Self::unloaded_event(), handler)
441    }
442
443    /// Occurs when the bounds (actual size) of the control have changed.
444    pub fn size_changed(
445        &self,
446        handler: impl Fn(&Interactive, &SizeChangedEventArgs) + 'static,
447    ) -> RoutedEventHandlerToken {
448        self.add_handler(Self::size_changed_event(), handler)
449    }
450
451    /// The boxing convention for a control held in an untyped value (a
452    /// `Content`, a `Tag`, an item): the value holds a `Ref<Control>`.
453    pub fn boxed(control: impl IntoRef<Control>) -> BoxedValue {
454        Rc::new(control.into_ref())
455    }
456
457    /// The control held by an untyped value, if it holds one (`value as
458    /// Control` upstream). See [`boxed`](Self::boxed).
459    ///
460    /// The value is a control whatever handle it holds it by: the
461    /// `Ref<Control>` of the boxing convention, the handle of another class
462    /// of the object model (a `Ref<Border>`, a `Ref<FerroObject>`), or an
463    /// interface handle that holds an object
464    /// ([`ValueTypes::register_interface_object`]); a value that a binding
465    /// passes on keeps the static type of its source.
466    pub fn from_boxed(value: &BoxedValue) -> Option<Ref<Control>> {
467        let value: &dyn AnyValue = &**value;
468        if let Some(control) = value.downcast_ref::<Ref<Control>>() {
469            return Some(control.clone());
470        }
471        ValueTypes::as_object(value)?.cast::<Control>()
472    }
473
474    /// The logical element held by an untyped value, if it holds one
475    /// (`value as ILogical` upstream): an element held by any handle, as
476    /// [`from_boxed`](Self::from_boxed) takes it.
477    pub fn logical_from_boxed(value: &BoxedValue) -> Option<Ref<StyledElement>> {
478        let value: &dyn AnyValue = &**value;
479        if let Some(control) = value.downcast_ref::<Ref<Control>>() {
480            return Some(control.clone().upcast());
481        }
482        if let Some(element) = value.downcast_ref::<Ref<StyledElement>>() {
483            return Some(element.clone());
484        }
485        ValueTypes::as_object(value)?.cast::<StyledElement>()
486    }
487
488    /// Ensures that the control is initialized and laid out when it is used
489    /// as the source of a visual brush while not attached to a visual root.
490    pub fn ensure_initialized(&self) {
491        if self.visual_root().is_none() {
492            if !self.is_initialized() {
493                for i in self.get_self_and_visual_descendants() {
494                    if let Some(c) = i.downcast_ref::<Control>() {
495                        if !c.is_initialized() {
496                            c.begin_init();
497                            c.end_init();
498                        }
499                    }
500                }
501            }
502
503            if !self.is_arrange_valid() {
504                self.measure(Size::INFINITY);
505                self.arrange(Rect::from_size(self.desired_size()));
506            }
507        }
508    }
509
510    fn loaded_processing_action() {
511        // Copy the loaded queue for processing. Two collections are used so
512        // that while one is being processed the other accepts adding new
513        // controls to process next: new controls can be added within the
514        // Loaded callback/event itself.
515        LOADED.with(|loaded| {
516            let mut loaded = loaded.borrow_mut();
517            let queue = std::mem::take(&mut loaded.queue);
518            loaded.queued.clear();
519            loaded.processing_queue.extend(queue);
520        });
521
522        // A panic from `on_loaded_core` (a Loaded handler or override)
523        // propagates to the dispatcher as usual, but the processing machinery
524        // must recover: controls left unprocessed are requeued and a new
525        // dispatcher job is posted for them, so a single faulty handler
526        // cannot stop Loaded from ever being raised again.
527        struct Finally;
528
529        impl Drop for Finally {
530            fn drop(&mut self) {
531                let restart = LOADED.with(|loaded| {
532                    let mut loaded = loaded.borrow_mut();
533                    let remaining = std::mem::take(&mut loaded.processing_queue);
534                    for control in remaining {
535                        loaded.add(control);
536                    }
537                    loaded.is_loaded_processing = false;
538
539                    // Restart if any controls were added to the queue while
540                    // processing.
541                    if !loaded.queue.is_empty() {
542                        loaded.is_loaded_processing = true;
543                        true
544                    } else {
545                        false
546                    }
547                });
548
549                if restart {
550                    Dispatcher::ui_thread().post_local(Control::loaded_processing_action, DispatcherPriority::LOADED);
551                }
552            }
553        }
554
555        let _finally = Finally;
556
557        loop {
558            let next = LOADED.with(|loaded| loaded.borrow_mut().processing_queue.pop_front());
559            match next {
560                Some(control) => control.on_loaded_core(),
561                None => break,
562            }
563        }
564    }
565
566    /// Schedules `on_loaded_core` to be called for this control. For
567    /// performance, it will be queued with other controls.
568    pub(crate) fn schedule_on_loaded_core(&self) {
569        if self.load_state.get() == LoadState::Unloaded {
570            let post = LOADED.with(|loaded| {
571                let mut loaded = loaded.borrow_mut();
572                let is_added = loaded.add(self.to_ref());
573                if is_added && !loaded.is_loaded_processing {
574                    loaded.is_loaded_processing = true;
575                    true
576                } else {
577                    false
578                }
579            });
580            self.load_state.set(LoadState::LoadPending);
581
582            if post {
583                Dispatcher::ui_thread().post_local(Self::loaded_processing_action, DispatcherPriority::LOADED);
584            }
585        }
586    }
587
588    /// Invoked as the first step of marking the control as loaded and
589    /// raising the `Loaded` event.
590    pub(crate) fn on_loaded_core(&self) {
591        if self.load_state.get() == LoadState::LoadPending && self.is_attached_to_logical_tree() {
592            self.load_state.set(LoadState::Loaded);
593
594            self.on_loaded(&RoutedEventArgs::with_event_and_source(Self::loaded_event(), self.to_ref()));
595        } else {
596            // We somehow got here while being detached?
597            self.load_state.set(LoadState::Unloaded);
598        }
599    }
600
601    /// Invoked as the first step of marking the control as unloaded and
602    /// raising the `Unloaded` event.
603    pub(crate) fn on_unloaded_core(&self) {
604        match self.load_state.get() {
605            LoadState::Loaded => {
606                self.load_state.set(LoadState::Unloaded);
607
608                self.on_unloaded(&RoutedEventArgs::with_event_and_source(Self::unloaded_event(), self.to_ref()));
609            }
610            LoadState::LoadPending => {
611                // Remove from the loaded event queue here as a failsafe in
612                // case the control is detached before the dispatcher runs
613                // the Loaded jobs.
614                let this = self.to_ref();
615                LOADED.with(|loaded| loaded.borrow_mut().remove(&this));
616
617                self.load_state.set(LoadState::Unloaded);
618            }
619            LoadState::Unloaded => {}
620        }
621    }
622
623    /// Clears the queue of controls waiting for their `Loaded` event. Since
624    /// tests reset the dispatcher instance, the callback might never arrive.
625    #[doc(hidden)]
626    pub fn reset_loaded_queue_for_unit_tests() {
627        LOADED.with(|loaded| {
628            let mut loaded = loaded.borrow_mut();
629            loaded.queue.clear();
630            loaded.queued.clear();
631            loaded.processing_queue.clear();
632            loaded.is_loaded_processing = false;
633        });
634    }
635}
636
637impl IDataTemplateHost for Control {
638    fn data_templates(&self) -> DataTemplates {
639        Control::data_templates(self)
640    }
641
642    fn is_data_templates_initialized(&self) -> bool {
643        Control::is_data_templates_initialized(self)
644    }
645}
646
647impl ISetterValue for Control {
648    fn initialize(&self, setter: &Setter) {
649        // A context menu implements the contract itself.
650        if let Some(context_menu) = self.downcast_ref::<ContextMenu>() {
651            return context_menu.initialize(setter);
652        }
653
654        if setter.property() == Some(Self::context_flyout_property().as_property()) {
655            return; // Allow ContextFlyout to not need wrapping in a template
656        }
657
658        panic!("Cannot use a control as a Setter value. Wrap the control in a <Template>.");
659    }
660}