I want to bind the Cursor of a Border to a private property of my TemplateInstance.
XAML:
<Border Cursor="{Binding ToggleCursor}">
C#:
private Cursor ToggleCursor {
get {
return IsEnabled ? Cursors.Hand : Cursors.Arrow;
}
}
I have also implemented INotifyPropertyChanged in my TemplateClass:
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string PropertyName) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(PropertyName));
}
}
And of course I notify the framework about the changes:
(Even if I wouldn't do that it should still get the property at least a single time)
IsEnabledChanged += (sender, e) => OnIsEnabledChanged();
and
private void OnIsEnabledChanged() {
OnPropertyChanged("ToggleCursor");
}
And yes, the class implements the interface.
The problem is, that the border never takes the value from the property.
F.e. if I return a Cursors.Cross in the property it still shows the Cursors.Arrow.
Any ideas?
Example:
This is some sort of checkbox with animated behaviour and the blue border recieves the curser binding. Problem solved - Forgot to set datacontext -__-
Regardless of whether you're re-templating an existing control or creating your own, you need a control style and also need to set a control template inside that style.
In the control template you can use a trigger to change things:
<Style TargetType="{x:Type YourCustomControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type YourCustomControl}">
<Border x:Name="Border">
... other elements, etc ...
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter TargetName="Border" Property="Cursor" Value="Hand" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then you don't need to create a custom property in your control class to do this.
ToggleCursor must be a public property and you need to call OnPropertyChanged("ToggleCursor") whenever the ToggleCursor property value changes (which seems to be related to IsEnabled property changes).
Typically this will be via the overridden OnPropertyChanged method:
private override void OnPropertyChanged(string PropertyName)
{
base.OnPropertyChanged(PropertyName);
switch (PropertyName)
{
case "IsEnabled":
OnPropertyChanged("ToggleCursor");
break;
}
}
Edit:
OP set the ToggleCursor property to public and also assigned the DataContext for TemplateInstance and this solved his issue.
Related
I have a WPF DataGridCheckBoxColumn, which is bound to an object that implements INotifyPropertyChanged as shown below:
DataGridCheckBoxColumn Binding="{Binding Path=IsSelected}" CellStyle="{StaticResource MyDataGridCheckBoxCellStyle}"/>
Here is the associated object:
public class ListItem : INotifyPropertyChanged
{
public int ID { get; set; }
private bool isSelected = false;
public bool IsSelected { get { return isSelected; } set { isSelected = value; OnChanged("IsSelected"); } }
public event PropertyChangedEventHandler PropertyChanged;
private void OnChanged(string prop)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
Everything works as expected, except that in order to set the Checkbox to Checked, I need to double click, in order to first select the column, and then set the checkbox value.
So, I decide to implement a Style trigger as shown below:
<Style x:Key="MyDataGridCheckBoxCellStyle" TargetType="DataGridCell">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True" >
<Setter Property="IsEditing" Value="True" />
</Trigger>
</Style.Triggers>
</Style>
Now I am able to single click to Check the Checkbox, but my binding doesn't work anymore. Any idea of what is going on here? Why does setting the Style Trigger remove the binding?
Changing the style can cause some issues with the default template. You would most likely need to copy the entire style + template and then modify that to suit your needs. You could try this http://wpf.codeplex.com/wikipage?title=Single-Click%20Editing however.
I'm having a problem where user input is interrupted by updates from the bound ViewModel.
The ViewModel exposes a numerical value which changes each second. This is bound to a TextBox in the view. We want the TextBox to display this value, which it does well.
However, when the user clicks on the TextBox and tries to enter a new value, the value in the model gets updated which causes the user-entered value in the textbox to be overwritten.
How can I most easily solve this issue, supporting user input and regular updates in the same control? I would appreciate code examples (C# / XAML).
If you need more details just ask :)
When TextBox gets focus (IsFocused property equals true) I change binding type so that it would not be updated unless it looses focus.
ViewModel:
class MainViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
Debug.WriteLine(value);
OnPropertyChanged();
}
}
public MainViewModel()
{
Task.Factory.StartNew(async () =>
{
for (int i = 0; i < 1000; i++)
{
Name = i.ToString();
await Task.Delay(3000);
}
});
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
XAML:
<StackPanel>
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Text" Value="{Binding Name, Mode=OneWay}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=IsFocused}" Value="True">
<Setter Property="Text" Value="{Binding Name, Mode=OneWayToSource, UpdateSourceTrigger=LostFocus}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<Button Content="Click"/>
</StackPanel>
I got sequence 2,3 then I inputed 56 and clicked Button so textbox lost focus and sent value to Name property. Debug printed following sequence:
1
2
3
56
4
In my application I'm using Prism and MahApps.metro. I created a RegionAdapter for FlyoutsControl, this is working like a charm.
The only problem is, that when I first navigate the View into Flyout Region, the Flyout pops up instead of sliding in from the side.
I can imagine this is because it is created at runtime and added to the FlyoutsControl at runtime, but is there a possibility to create the Flyout, add it to the FlyoutsControl and then show it via Slide-In effect?
Just setting the IsOpen property to false and then to open doesn't work :(
I was trying to do the same thing and was having issues but later i figured out the solution.
First, define the region name FlyoutRegion on FlyoutsControl.
<mahApps:MetroWindow ...>
<mahApps:MetroWindow.Flyouts>
<mahApps:FlyoutsControl prism:RegionManager.RegionName="FlyoutRegion">
<mahApps:FlyoutsControl.ItemContainerStyle>
<Style TargetType="{x:Type mahApps:Flyout}">
<Setter Property="Header" Value="{Binding Header}" />
<Setter Property="IsOpen" Value="{Binding IsOpen}" />
<Setter Property="Position" Value="{Binding Position}" />
</Style>
</mahApps:FlyoutsControl.ItemContainerStyle>
</mahApps:FlyoutsControl>
</mahApps:MetroWindow.Flyouts>
</mahApps:MetroWindow>
Create the RegionAdapter and register it in the Bootstrapper.
[Export]
public class FlyoutsControlRegionAdapter : RegionAdapterBase<FlyoutsControl>
{
[ImportingConstructor]
public FlyoutsControlRegionAdapter(IRegionBehaviorFactory factory)
: base(factory)
{
}
protected override void Adapt(IRegion region, FlyoutsControl regionTarget)
{
region.ActiveViews.CollectionChanged += (s, e) =>
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
foreach (FrameworkElement element in e.NewItems)
{
Flyout flyout = new Flyout();
flyout.Content = element;
flyout.DataContext = element.DataContext;
regionTarget.Items.Add(flyout);
}
}
};
}
protected override IRegion CreateRegion()
{
return new AllActiveRegion();
}
}
Inside Bootstrapper
protected override RegionAdapterMappings ConfigureRegionAdapterMappings()
{
var mappings = base.ConfigureRegionAdapterMappings();
mappings.RegisterMapping(typeof(FlyoutsControl), Container.GetExportedValue<FlyoutsControlRegionAdapter>());
return mappings;
}
Finally, register the desired View with FlyoutRegion.
regionManager.RegisterViewWithRegion("FlyoutRegion", typeof(FlyoutView));
The trick here is to expose the Header, IsOpen and Position properties in the ViewModel and associate it with FlyoutView.
You can refer the detail on this Code Project Link
View Model
public class MyViewModel
{
public string MyProperty { get; set; }
}
XAML
<CheckBox IsChecked="{Binding !MyProperty.Equals('Steve')}" />
Is this possible? How?
This sort of thing can be done (and many say should be done) in Xaml without involving logic from the View Model. To see it work, create a View Model like this...
public class ViewModel : INotifyPropertyChanged
{
private string _myProperty;
public string MyProperty
{
[DebuggerStepThrough]
get { return _myProperty; }
[DebuggerStepThrough]
set
{
if (value != _myProperty)
{
_myProperty = value;
OnPropertyChanged("MyProperty");
}
}
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name)
{
var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
And then bind it to some Xaml that looks like this...
<Grid>
<CheckBox Content="Some check box">
<CheckBox.Style>
<Style TargetType="CheckBox">
<Setter Property="IsChecked" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding MyProperty}" Value="Steve">
<Setter Property="IsChecked" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</CheckBox.Style>
</CheckBox>
</Grid>
This is a standard WPF Checkbox that has been styled with a data trigger. The trigger will set the IsChecked property to true whenever the 'MyProperty' property contains "Steve". Otherwise the CB will be unchecked (per the overriding Setter in the Style). It works because the trigger listens to changes in the VM's 'MyProperty'. So visualization is entirely relegated to the user surface.
Triggers can be combined (and even used with Template Selectors) to access powerful functions built-in to WPF; and they will bind to any dependency property on the Check box, like Background colour etc.
A lot of people will suggest a converter, which certainly works. But I've found a much quicker way is to create a new bool property to use and bind to that:
public string MyProperty{get;set;}
public bool MyPropertyChecked
{
get { return !MyProperty.Equals('Steve')}
}
I want to make a custom TextBox using XAML and a custom class with an additional property to TextBox called PosType. PosType will be rendered inside the red triangle in the side.
The TextBox should be an ordinary textbox with enough margin from left to not intercept the other text.
Here is the Image showing the desired look of the textbox.
The Control class :
public class PosTextBox : TextBox
{
public string PosType { get; set; }
}
**The style I wrote : (quite similar approach to what I want except here I used border and other parts may not be accurate. **
xmlns:Pro="clr-namespace:Prox.XamlControls">
<!-- Custom TextBox -->
<Style x:Key="c1PosTextBox" TargetType="{x:Type Pro:PosTextBox}" >
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="Foreground" Value="White" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Pro:PosTextBox}">
<Grid>
<Border>
<Border>
<TextBlock Text ="{TemplateBinding Path= Pro:PosType}"></TextBlock>
<!--<TextBlock Text ="{TemplateBinding ElementName=Pro:PosTextBox, Path= Pro:PosType}"></TextBlock>-->
</Border>
</Border>
<Border Margin="5,10,5,10">
<ContentPresenter Name="Content" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" TextBlock.Foreground="White"></ContentPresenter>
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How to create this custom textbox and achieve the desired look?
Edit:
Please guide me to fix the minor issues based on the same approach I mentioned above.
You could do that using Adorners
Adorners are rendered in a different layer called AdornerLayer on top of the UIElement, Which can get you the desired affect.
public class PosTypeAdorner : Adorner
{
private string _posText;
// Be sure to call the base class constructor.
public PosTypeAdorner (UIElement adornedElement, string posText) : base(adornedElement)
{
_posText = posText;
}
// A common way to implement an adorner's rendering behavior is to override the OnRender
// method, which is called by the layout system as part of a rendering pass.
protected override void OnRender(DrawingContext drawingContext)
{
// Draw the red triangle with it's text using the drawingContext here
}
}
Assuming you want the text of the PosType to be bindable, you should make it as a Dependency property.
Use OnApplyTemplate to attach the adorner to your text box
public class PosTextBox : TextBox
{
public PosTextBox()
{
}
public static readonly DependencyProperty PosTypeProperty =
DependencyProperty.Register("PosType", typeof (string), typeof (PosTextBox), new PropertyMetadata(default(string)));
public string PosType
{
get { return (string)GetValue(PosTypeProperty); }
set { SetValue(PosTypeProperty, value); }
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var layer = AdornerLayer.GetAdornerLayer(this);
var posAdorner = new PosTypeAdorner(this, PosType);
layer.Add(posAdorner);
}
}
For more information, you can check out this links:
http://www.codeproject.com/Articles/54472/Defining-WPF-Adorners-in-XAML
http://www.nbdtech.com/Blog/archive/2010/06/21/wpf-adorners-part-1-ndash-what-are-adorners.aspx
http://www.nbdtech.com/Blog/archive/2010/06/28/wpf-adorners-part-2-ndash-placing-any-control-on-the.aspx
Good luck