Hyperlinks are staying inactive after setting isEnabled=true to parent control - wpf

I've got a TabItem contanining a listbox, which has an obeservable collection of my feeds class as its item source. When I refresh/load the feeds into the collection I want to disable the main window so that the user can't go clicking other things while this process is running. So I set tbCtrl.isEnabled=false; to my tab control on the form. Then assign an event handler to the a custom finish event which is triggered after all the feeds are loaded.
This all works fine, however the hyperlinks for the results which are currently displayed on the tab control never get re-enabled (Nor do the next few which are out of view due to the list box size). All the other results further down are fine, as are the results on the other tab.
I've tried calling InvalidateVisual on the tab control after everything is finished, to see if that makes a difference but that doesn't seem to cause any change.
I could understand it if it was all Hyperlinks doing it, or just the ones currently displayed, but I don't understand why ones which are out of scroll are not working either.

I hit the same issue.
What I did is to bind HyperLink's IsEnabled to the parent, and put that in an App global resource.
<Style TargetType="{x:Type Hyperlink}">
<Setter Property="IsEnabled" Value="{Binding IsEnabled, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type FrameworkElement}}}" />
</Style>

I found the answer for my case of the hyperlink not getting re-enabled, not sure if it applies to yours:
I found that when the Hyperlink's parent control is disabled (IsEnabled=false), the Hyperlink will not get notified of changes, e.g. IsEnabledChanged does not get fired, even when the bound property changes value.
My solution was to change my Xaml to no longer disable the ancestor control (which was causing the Hyperlink's parent to be disabled). With the parent (TextBlock) always enabled, now Hyperlink updates properly always.
(I'm a little bothered that the IsEnabled binding behaves differently than Controls do, and I'm not sure what I would do if I couldn't leave the ancestor enabled... but at least this lets me understand the issue I was having, and lets me work around it.)
Details: WPF 3.5 SP1

It's not just HyperLinks. It seems to be more specifically TextBlock which of course is what you use to wrap a HyperLink in WPF. This will give the same issue :
<TextBlock>
<Run Text="Barcode:"/>
<InlineUIContainer BaselineAlignment="Center">
<TextBox Text="{Binding OriginalPackage.BarcodeNumber}" />
</InlineUIContainer>
</TextBlock>
I was hoping setting IsEnabled="True" would fix it but it doesn't seem to.
The easy solution is to use a StackPanel with Orientation="Horizontal"

Related

Static resource array binding only works for the first time

I created a custom control which inherits from toolbar.
I would like that the default control template of the toolbar will contain a couple of default buttons.
In order to achieve this, I created a static array to hold the button list:
<x:Array x:Key="toolbarButtons" Type="{x:Type ToggleButton}">
<ToggleButton Content="Bold"
Command="{x:Static ns1:EditingCommands.Bold}"
CommandTarget="{Binding}"
IsChecked="{Binding IsBold, Mode=TwoWay}"/>
<ToggleButton Content="Italic"
Command="{x:Static ns1:EditingCommands.Italic}"
CommandTarget="{Binding}"
IsChecked="{Binding IsItalic, Mode=TwoWay}"/>
</x:Array>
The toolbar control has datacontext which is bounded to a text editor which includes all of the command bindings and the boolean dependency properties (IsBold, IsItalic).
I set the Toolbar ItemSource to use the array like this:
<Setter Property="ItemsSource" Value="{StaticResource toolbarButtons}"/>
Now, when I open a window who hosts the toolbar for the first time on a given run, everything works great.
The problem is, when I close the window, and reopen it, the button bindings stop working (IsCheked property stops being connected to the dependency property).
I used snoop to check the bindings, and it says that the value of IsChecked is local, which means the binding is ignored.
I suspect that the problem is my array is a static resource, so the toolbar uses the same instance from time to time, and this somehow ruins the binding.
My question is how to solve this, or maybe should I use a different approach in order to achieve default buttons for my toolbar?
I think the problem is that when you declare your array in XAML, it's only instantiated once. So, the second toolbar (and on) are trying to use the exact same objects. When it's reused, your bindings are likely being overwritten.
Have you tried adding x:Shared="False" to the array declaration?
<x:Array x:Key="toolbarButtons" x:Shared="False" Type="{x:Type ToggleButton}">

WPF MVVM: How to enable/disable buttons in ListBox if I'm using a DataTemplate

I have a WPF/MVVM app with a ListBox which displays data through a DataTemplate. I managed to change the selected item in the ListBox when pressing a button so the CommandParameter is linked to the ListBox's SelectedItem, but I cannot get the buttons to be enabled/disabled correctly in the same way. For example, if I have 2 items and the button should be enabled in one and disabled in the other, when I select an element BOTH buttons have the same state, and they BOTH change state when I select another item.
I am using a RelayCommand as used in many MVVM Frameworks.
Here is my XAML (removed "not interesting" parts):
<UserControl.Resources>
<DataTemplate x:Key="ItemTemplate">
<Grid>
<Button Content="Something" Name="EnabledDisabledButton" Click="Button_Click"
Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type UserControl}}, Path=DataContext.SomeCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBox}}, Path=SelectedItem}"/>
</Grid>
</DataTemplate>
<Style TargetType="{x:Type ListBoxItem}" x:Key="ContainerStyle">
<Setter Property="ContentTemplate" Value="{StaticResource ItemTemplate}" />
</Style>
</UserControl.Resources>
<ListBox x:Name="myListBox" ItemsSource="{Binding ElementList}"
IsSynchronizedWithCurrentItem="True" ItemContainerStyle="{StaticResource ContainerStyle}"/>
I tried to pass the SelectedItem as a parameter to the RelayCommand's CanExecute method, but the result was the same as before.
Is there a way to pass the actual ListBoxItem in which the button "lives in" as a parameter to the command, so each one will be processed separately by the CanExecute method? Would it work if I got this? (right now I am handling the Click event to select the correct item in the list before executing the command).
In my CanExecute method I am evaluating some property of the SelectedItem in order to enable/disable the corresponding button. An alternative would be to evaluate this property for all elements, but I cannot think of a way to do it inside the ViewModel, and then communicate to the view the result (if it is even possible while using a DataTemplate for the items).
Thanks for your input, regards!
Converting My comment into an answer:
Why not just CommandParameter="{Binding}"?
You mention "MVVM" in the question, but it seems you use the MVVM way to your full advantage.
I would not have a Button_Click event in the style at all. That is because it is in fact a style, which per definition could be changed to another style which does not have the same event, which again will make the application stop working as wanted if you choose to have a style-based app in the future.
A rule I use is that a style is a style. A style has to do with the UI and "looks" of the app.
Functionality should be separate from the UI. The programmer can define the Command, and the designer can decide how the user will use that in the best way.
That's exactly where the code separation from the MVVM pattern cames into grip.
To separate the "looks" and user behavior and the app's logic.
Like...it should not matter to the model if a command fires from a button, a menu, a datacontext or a key stroke.
If this particular problem was handled to ME, I would solve it by having a HOLDER-class.
This is a class (DependencyObject which implements INotifyPropertyChanged) that holds a ICommand property as well as the "row" that will be displayed in the various rows in the ListBox.
The ICommand property will be bound to the Button, having the row (class) itself as CommandParameter to the call.
Then the actual row would be used in the ItemTemplate on the ListBox, with Bindings to different elements (proprty with or withouy Converters) to make whatever desired display available.
I hope I explained good enough...
Feel free to ask more if you want more details to my solution alternative.

Unable to dynamically set focus in WPF MVVM application

I'm working on a WPF application using the MVVM pattern, and I have hit a problem with focus.
I have full screen slide on 'overlays' and within each overlay I have a ContentPresenter that I use to display arbitrary view models/views by data binding it's content to a view model property I set in my data context, like so:
<Grid Name="OverlayContainer"
FocusManager.IsFocusScope="True"
KeyboardNavigation.TabNavigation="Cycle"
IsEnabled="False">
<Grid.RenderTransform>
<TranslateTransform x:Name="OverlayContainerTransform"
X="{Binding ElementName=OverlayContainer, Path=ActualWidth}"
Y="0"/>
</Grid.RenderTransform>
<ContentPresenter x:Name="OverlayContent" Content="{Binding Path=OverlayViewModel"/>
</Grid>
I can then dynamically set the OverlayViewModel property to various view models when needed and use data templates to get WPF to automatically display the correct view for the relevant view model:
<DataTemplate DataType="{x:Type ViewModels:AuthorisatonViewModel}">
<Views:AuthorisatonView/>
</DataTemplate>
When I change my view model and slide on the overlay I set focus scope to the overlay focus scope (which I seem to be able to do OK with OverlayContainer.Focus), but the problem I'm having is that I can't seem to get keyboard focus to go to the first focusable element on the relevant view, whatever it may be.
I thought I'd found what I needed here but when using this code to walk the visual tree it seems that when passing the ContentPresenter (OverlayContent) into VisualTreeHelper.GetChildrenCount() that it returns no children, so it can't get down the elements within the dynamic view.
I've also tried setting the OverlayContainer as the active focus scope and then calling:
OverlayContainer.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next))
but that failed to work also.
All the views I am trying to display have at least 1 focusable, visible, enabled element in them.
Any ideas?
It appears Rachel was spot one - it turned out it was that the view hadn't rendered yet, so the child visual elements weren't available yet. Calling my focuser like so:
// Focus on first child element only once rendered
this.OverlayContainer.Dispatcher.BeginInvoke(DispatcherPriority.Render,
new Action<FrameworkElement>(Focuser.FocusOnFirstFocusableChild),
this.OverlayContainer);
allowed me to access all the child visual elements and set focus accordingly.

Losing the binding for radiobutton after I set the property manually in code

I have a list on my WPF xaml which contains two items. Below is the Style template for each item. Now on UI this shows like a group of radio buttons(No. of radio buttons depends on no. of items in my list).
<Style x:Key="RadioButtonListBoxItemStyle" TargetType="{x:Type ListBoxItem}" >
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<RadioButton FlowDirection="LeftToRight"
Margin="10 15"
Content="{Binding Value}"
GroupName="{Binding DisplayGroupName}"
IsChecked="{Binding IsSelected, Mode=TwoWay}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Now I bind a list(having 2 items) using the above style template to get two radio buttons. What happens is everything works pretty fine i.e when I change the selection of radio button on UI the IsSelected property is getting updated properly to true or false depending on whether my radio is checked/un-checked. But if I try to set the list in the code manually, then from that point my binding of the radio button's with my list is lost and nothing happen's.
Any help on this would be great and based on my needs I have to set the list in the code manually. So is there any solution in a way that binding will not be lost even though I set the list in my code manually. Thanks.
-Ady.
This is a common problem with radio buttons in WPF, and it has to do an unusual aspect of binding, one that is marginally more feature than bug.
The design of binding assumes that the only two things that change the value of a binding's target property are a) actions in the UI and b) changes to the source property. If you set the target property of a binding in code - like, you explicitly set the Background of a Border, even though it has a binding - the binding decides that you know what you're doing, and that it should just get out of the way. So it turns itself off.
This is a pretty sensible design decision, for the most part. It's better than throwing an exception, for instance. Most of the time, you're not going to ever set IsEnabled in code anyway; you'll let the binding do it. Especially if you're using MVVM.
Okay, so what happens if you have radio buttons in a group?
When you check one button in the group, the WPF code that manages radio button groups unchecks all the other buttons in the group, by setting IsChecked to false in code. The binding disables itself. Oops.
Here's the solution: If you're using radio buttons and binding, don't use groups. Handle the mutual exclusion logic in your view model code. In your case, code your view models so that only one object in a collection can have IsSelected true at any given time. (Yes, this is a pain.)
The radio buttons will still work as expected, but since the only properties being set by code are the source properties, binding won't break.
you are setting the style for the listboxitem class, including the bindings. so, when you set the list from code behind it does not contain listboxitems, it contains the items from your list. so, the style does not apply. what you should do is make the <DataTemplate> for the type of item in your list--in effect telling WPF what you want each item to look like.
<DataTemplate TargetType="{x:Type MyCustomClass}" >
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding Deleteable, Mode=TwoWay}" />
<Label Content="{Binding Name}" />
</StackPanel>
</DataTemplate>
</DataTemplate>
(this is off the top of my head, so the xaml might not be exactly right)

WPF Binding Help

I haven't used WPF that much so the solution to this is probably pretty easy.
In the ide I'm developing it will have multiple controls(text editor) each being hosted in a tab, much like VS does for each source file. When the user clicks new the "host" creates a new EditorWindow(a usercontrol), creates a new tab, and tells the tab to display the EditorWindow it created, and then updates a property called currentWindow (of type EditorWindow) with the one that's currently active. Inside the EditorWindow is the text editor whose name is textEditor(also a property). What I'm trying to do is take this code from the quick start source of the text editor control I'm using
<StackPanel>
<CheckBox Checked="EditiorOptionsChecked" IsChecked="{Binding ElementName=Control, Path=currentWindow.textEditor.IsIndicatorMarginVisible}" Content="Indicator margin visible" />
<CheckBox Checked="EditiorOptionsChecked" IsChecked="{Binding ElementName=Control, Path=currentWindow.textEditor.IsLineNumberMarginVisible}" Content="Line number margin visible" />
<CheckBox Checked="EditiorOptionsChecked" IsChecked="{Binding ElementName=Control, Path=currentWindow.textEditor.IsRulerMarginVisible}" Content="Ruler margin visible (useful for fixed-width fonts only)" />
<CheckBox Checked="EditiorOptionsChecked" IsChecked="{Binding ElementName=Control, Path=currentWindow.textEditor.IsSelectionMarginVisible}" Content="Selection margin visible" />
</StackPanel>
put that in the host controls xaml, and bind the checkboxes to the syntax editor. I've tried a couple different things to no avail. Control is the name of the window hosting all the tabs, and path is obviously supposed to be the property that the checkboxes are bound too. I'm pretty sure the problem is that at initial run-time currentWindow isn't initialized so therefore my bindings aren't ever getting updated, but I'm at a loss as to how to fix this issue. Thanks!
Since you are new to WPF, you may not know that properties have to implement some sort of change notifications in order for bindings to work. For instance, if any of the properties in the the path "currentWindow.textEditor.IsIndicatorMarginVisible" change, you need to inform the binding engine that it has changed. If you implement these properties as DependencyPropertys, the change tracking comes for free. Otherwise, you should implement INotifyPropertyChanged.
I've found that the Snoop utility is the easiest way to do quick binding debugging, you should try using it and see if it tells you anything useful on the bound properties.

Resources