I'm programming in WPF(C#). I want to alert users about filling empty text box (or any other controls). I want to flash control to alert him/her. This is the codes that I used them but it does not change color:
static void AlertByChangingBackground(Control control)
{
Action a = () =>
{
control.Background = System.Windows.Media.Brushes.Red;
Thread.Sleep(500);
control.Background = System.Windows.Media.Brushes.White;
};
control.Dispatcher.Invoke(a);
}
As it can be seen, I also use Action but it does not work. I also use control.UpdateLayout() before Sleep method but it does not working, too. How can I fix the problem.
Update 1:
Now, I use codes illustrated below. But the problem is when the function is called several times (specially when it is called continuously in short times) the color of text does not back to its first color. For example my control may be remain at red color. How can I fix it?
public static void AlertByChangingBackground(Control control)
{
Action a = () =>
{
ColorAnimation animation;
animation = new ColorAnimation();
animation.From = Colors.Red;
animation.To = ToColor(control.Background);
animation.Duration = new Duration(new TimeSpan(0, 0, 0, 0, 330));
RepeatBehavior rb = new RepeatBehavior(3);
animation.RepeatBehavior = rb;
control.Background = new SolidColorBrush(Colors.Red);
control.Background.BeginAnimation(SolidColorBrush.ColorProperty, animation);
};
control.Dispatcher.BeginInvoke(a);
}
I note that I want to start animation from current background of my control, not from white or any predefined color.
You can use triggers or animations to alert the user rather than using thread,
you can add xmlns:sys="clr-namespace:System;assembly=mscorlib" namespace for checking the string is empty or not.
<Style TargetType="{x:Type TextBox}" x:Key="AlertStyle">
<Style.Triggers>
<DataTrigger Binding="{Binding Text,RelativeSource={RelativeSource Mode=Self}}" Value="{x:Static sys:String.Empty}">
<Setter Property="Background" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
use this style for your TextBox control like follows,
<TextBox Width="100" Height="25" Style="{StaticResource AlertStyle}">
Related
I have a DataGrid with its RowStyle set in XAML:
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}" BasedOn="{StaticResource {x:Type DataGridRow}}">
<Setter Property="VerticalAlignment" Value="Center"/>
<Style.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="LightSteelBlue"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Aquamarine"/>
<Setter Property="Foreground" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
The DataGrid contains rowdetails which I wish to print when that row is selected. However, I need to change the background colour of the row (including its rowdetails) from Aquamarine to White when printing and then back again after printing. I have the following method to achieve this:
private void ChangeStyle(bool printing)
{
Style rsRowStyle = new Style();
Style oldStyle = dgdCustomersListing.RowStyle;
Trigger DataTrigger = new Trigger();
DataTrigger.Property = DataGridRow.IsSelectedProperty;
DataTrigger.Value = true;
rsRowStyle.Triggers.Add(DataTrigger);
Setter TriggerSetter = new Setter();
TriggerSetter.Property = DataGridRow.BackgroundProperty;
TriggerSetter.Value = Brushes.LightGray;
rsRowStyle.Setters.Add(TriggerSetter);
dgdCustomersListing.RowStyle = printing ? rsRowStyle : oldStyle;
}
This works fine when first called for printing (the row background converts to white), but calling the method again when printing is false fails to revert the DataGrid back to the original style.
Why does it not work?
My error: the assignment to oldStyle needs to be made when first calling the printing method, and then passing that value to the ChangeStyle method so that it is not changed by that method. Hence:
private void ChangeStyle(bool printing, Style oldStyle)
{
Style rsRowStyle = new Style();
Trigger DataTrigger = new Trigger();
DataTrigger.Property = DataGridRow.IsSelectedProperty;
DataTrigger.Value = true;
rsRowStyle.Triggers.Add(DataTrigger);
Setter TriggerSetter = new Setter();
TriggerSetter.Property = DataGridRow.BackgroundProperty;
TriggerSetter.Value = Brushes.LightGray;
rsRowStyle.Setters.Add(TriggerSetter);
dgdCustomersListing.RowStyle = printing ? rsRowStyle : oldStyle;
}
I have left this up as Q&A as it took me some time to sort out how to change the style in code, and I hope this might be useful to others.
I'm trying to implement custom Border, which binds to bool property in ViewModel, and whenever this property changes I want to do some animation with Border.
ViewModel property has OnPropertyChanged interface, It looks like this:
public bool Enable_control
{
get { return _enable_ctl; }
set { _enable_ctl = value; OnPropertyChanged(); }
}
private bool _enable_ctl;
This is how I bind custom Border in xaml:
<cust_border:Border_slide ShowSlide={Binding Enable_control}"/>
And this is my custom border control code:
public class Border_slide : Border
{
public Border_slide()
{
}
public bool ShowSlide
{
get { return (bool)GetValue(ShowSlideProperty); }
set { SetValue(ShowSlideProperty, value);}
}
public static readonly DependencyProperty ShowSlideProperty =
DependencyProperty.Register("ShowSlide", typeof(bool), typeof(Border_slide), new
FrameworkPropertyMetadata(true, new PropertyChangedCallback(ShowSlideChanged)));
private static void ShowSlideChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//I would like to do animation of control when property of ViewModel changes - presumably here,
//but PropertyChangedCallback gets triggered only once - not on every Enable_control change!
}
}
So, question is: how do you properly update UserControl's dependency property from Viewmodel property change, in order to do something with UserControl next?
There was a time when I did something similar to yours.
In my case, I hope it will help since I solved it this way. But it can be different from what you think, so please let me know if there's anything I missed!
And I have uploaded my sample source code to GitHub.
Here sample sourcecode. https://github.com/ncoresoftsource/stackoverflowsample/tree/main/src/answers/dependency-border-animation
ViewModel
public class MainViewModel : ObservableObject
{
private bool _enable_ctl;
public bool Enable_control
{
get { return _enable_ctl; }
set { _enable_ctl = value; OnPropertyChanged(); }
}
}
MainWindow
I used Label instead of Border. Because Border cannot use ControlTemplate, it can be constrained to use and expand animation.
<CheckBox Content="Switch" IsChecked="{Binding Enable_control}"/>
<Label Style="{StaticResource SLIDER}"/>
App.xaml
<Style TargetType="Label" x:Key="SLIDER">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="#AAAAAA"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Width" Value="100"/>
<Setter Property="Height" Value="100"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Label">
<Border x:Name="border" Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Opacity="0">
<ContentPresenter/>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Enable_control}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="0" To="1" Duration="00:00:0.5"
Storyboard.TargetName="border"
Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard>
<Storyboard>
<DoubleAnimation From="1" To="0" Duration="00:00:0.5"
Storyboard.TargetName="border"
Storyboard.TargetProperty="Opacity"/>
</Storyboard>
</BeginStoryboard>
</DataTrigger.ExitActions>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Solved. Clemens was partially right - nothing was wrong with my code, property did get notified with changes. My problems were elsewhere - with a variable that didn't get value before property changed, and default value of dependency property. I sorted out those things now, and It works as expected.
Here is my full code, might get useful to someone, at least I find It so:
It's a custom Border, which can be used for a sliding animation of left Margin. Meaning you can use this Border to slide some menues to View from left side to desired right location, just add controls inside It and you're ready to go. Code can be used for other controls too (Panel etc.), but I used Border since I can round corners or do other nice UI presentations with it easily.
Logic behind is that If you set MoveTo property, Border will slide to desired left Margin location. If you don't, then Border will slide to center of screen - meaning you can use this Border for sliding menues into Views that are centered or left aligned in yout MainWindow. That was actually whole point of creating It - because you cannot use dynamic values in animation To or From, since these are freezables. So I had to create this custom control, because I couldn't do an animation to screen center (width needs to be calculated first). Anyway, here It is:
public class Border_slide : Border
{
public Border_slide()
{
Loaded += Border_slide_Loaded;
}
private void Border_slide_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
//Save starting Margin.Left
StartX = Margin.Left;
}
///<summary>Property for starting X location</summary>
private double StartX
{
get { return _startX; }
set { _startX = value; }
}
private double _startX;
public static DependencyProperty MoveToProperty =
DependencyProperty.Register("MoveTo", typeof(double), typeof(Border_slide), new PropertyMetadata(default(double)));
///<summary>Property thats sets to what Margin.Left we want move Border</summary>
public double MoveTo
{
get { return (double)GetValue(MoveToProperty); }
set { SetValue(MoveToProperty, value); }
}
public bool ShowSlide
{
get { return (bool)GetValue(ShowSlideProperty); }
set { SetValue(ShowSlideProperty, value); }
}
public static readonly DependencyProperty ShowSlideProperty =
DependencyProperty.Register("ShowSlide", typeof(bool), typeof(Border_slide), new FrameworkPropertyMetadata(true,
new PropertyChangedCallback(ShowSlideChanged)));
private static void ShowSlideChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var brd = d as Border_slide;
//Animate Margin, when Property changes
if (((bool)e.NewValue) == true) //When property is true, Border is allready at desired location, so we move It to first x- location
{
ThicknessAnimation back_to_start_X_location = new ThicknessAnimation
{
BeginTime = TimeSpan.FromSeconds(0),
Duration = TimeSpan.FromSeconds(1),
From = new Thickness(brd.Margin.Left, 0, 0, 0),
To = new Thickness(brd.StartX, 0, 0, 0)
};
brd.BeginAnimation(Border.MarginProperty, back_to_start_X_location);
}
else //If property is False we need to move Border to desired X- location
{
//If we don't set MoveTo property then move Border to center of screen
if (brd.MoveTo == default(double))
{
var X_center = Application.Current.MainWindow.ActualWidth / 2 - brd.Width / 2;
ThicknessAnimation to_center_X_location = new ThicknessAnimation
{
BeginTime = TimeSpan.FromSeconds(0),
Duration = TimeSpan.FromSeconds(1),
From = new Thickness(brd.ZacetniX, 0, 0, 0),
To = new Thickness(X_center, 0, 0, 0)
};
brd.BeginAnimation(Border.MarginProperty, to_center_X_location);
}
else //If MoveTo property is set then move Border to desired X-location
{
ThicknessAnimation to_desired_X_location = new ThicknessAnimation
{
BeginTime = TimeSpan.FromSeconds(0),
Duration = TimeSpan.FromSeconds(1),
From = new Thickness(brd.StartX, 0, 0, 0),
To = new Thickness(brd.MoveTo, 0, 0, 0)
};
brd.BeginAnimation(Border.MarginProperty, to_desired_X_location);
}
}
}
}
So, as you see - Border moves depending on what you set as start Margin.Left (that is a must) and/or MoveTo property, with binding to some bool value from ViewModel. In my case, I slide this Border when I disable all my other controls in View - so that I can make blur effect on View too. If you will use It for different things, than be careful about default value of ShowSlide property (mine is true). And surely you can re-adjust control for different things too...
E.g. usage in XAML:
<my_controls:Border_slide Margin="-500,0,0,0" MoveTo="5" ShowSlide="{Binding Enable_control}">
In C# UWP I am creating custom tooltip style.
I have changed the default style of tooltip as below.
<Style TargetType="ToolTip">
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="{ThemeResource SystemControlBackgroundChromeMediumLowBrush}" />
<Setter Property="BorderBrush" Value="{ThemeResource SystemControlForegroundChromeHighBrush}" />
<Setter Property="BorderThickness" Value="{ThemeResource ToolTipBorderThemeThickness}" />
<Setter Property="FontSize" Value="{ThemeResource ToolTipContentThemeFontSize}" />
<Setter Property="Padding" Value="40,40,40,35"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<Grid Background="Transparent">
<Grid
MinWidth="100"
MinHeight="90"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}"
Padding="15"
Background="Transparent">
<local:ArrowDown x:Name="arrowDown" TooltipPlacement="{TemplateBinding Placement}"/>
And my custom control ArrowDown is getting information of ToolTip placement, so I can show it depends if tooltip is under or above control.
In the ArrowDown control I have added a DependencyProperty as below:
public PlacementMode TooltipPlacement
{
get { return (PlacementMode)GetValue(TooltipPlacementProperty); }
set { SetValue(TooltipPlacementProperty, value); }
}
public static readonly DependencyProperty TooltipPlacementProperty =
DependencyProperty.Register("TooltipPlacement", typeof(PlacementMode), typeof(ArrowDown), new PropertyMetadata(null, TooltipPlacementChangedCallback));
private static void TooltipPlacementChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var self = (ArrowDown)d;
self.CalculateArrowVisibility();
}
// Method to show or hide arrow
private void CalculateArrowVisibility()
{
}
And the problem is that the CalculateArrowVisibility is fire only the first time when tooltip is shown, and it always returns Top for TooltipPlacement, no matter if tooltip is shown below or above control.
I need CalculateArrowVisibility to be fired whenever the tooltip is shown, and I need TooltipPlacement property to show if tooltip is Under or Above control.
Anyone have the idea about this?
The fact is that you cannot use the ToolTipService attached properties (e.g. <Button ToolTipService.Placement="Bottom" ToolTipService.ToolTip="!!!" />) to define the tooltip and it placement. This way the Placement is not set on the actual ToolTip control itself, and that's why it will always return Top.
In order to have the ToolTip pass down its Placement value to your custom dependency property, you will have to attach it like the following -
<Button>
<ToolTipService.ToolTip>
<ToolTip Placement="Bottom" Content="Hahaha..." />
</ToolTipService.ToolTip>
</Button>
Update
Turns out that even though the app Window pushes the tooltip above or below its parent, its Placement value is never changed, what's changed is its horizontal & vertical offsets.
So, in your case, if we could work out its exact vertical offset, we would be able to determine whether the tooltip is above or below (its parent).
Given we have a ToolTip Style in place, we can create an attached property of type ToolTip and attach it to the Grid that contains the ArrowDown control.
<Grid MinWidth="100"
MinHeight="90"
Height="{TemplateBinding Height}"
Width="{TemplateBinding Width}"
Padding="15"
Background="Transparent"
local:ToolTipHelper.ToolTip="{Binding RelativeSource={RelativeSource TemplatedParent}}">
Because the TemplatedParent of the Grid is the ToolTip, we can use RelativeSource binding to link the ToolTip on the screen with our attached property, as shown above.
Now, we have a reference to the actual ToolTip, let's find its offsets. After some digging, I've found that the offsets of the ToolTip are always 0, they are useless; however, the offsets of its parent - a Popup, sometimes gives me the correct values, but not always. This is because I was using the Opened event where those values weren't yet populated; as soon as I changed it to SizeChanged, they have been giving me the expected values.
public static class ToolTipHelper
{
public static ToolTip GetToolTip(DependencyObject obj)
{
return (ToolTip)obj.GetValue(ToolTipProperty);
}
public static void SetToolTip(DependencyObject obj, ToolTip value)
{
obj.SetValue(ToolTipProperty, value);
}
public static readonly DependencyProperty ToolTipProperty =
DependencyProperty.RegisterAttached("ToolTip", typeof(ToolTip), typeof(ToolTipHelper),
new PropertyMetadata(null, (s, e) =>
{
var panel = (Panel)s; // The Grid that contains the ArrowDown control.
var toolTip = (ToolTip)e.NewValue;
// We need to monitor SizeChanged instead of Opened 'cause the offsets
// are yet to be properly set in the latter event.
toolTip.SizeChanged += (sender, args) =>
{
var popup = (Popup)toolTip.Parent; // The Popup that contains the ToolTip.
// Note we have to use the Popup's offset here as the ToolTip's are always 0.
var arrowDown = (ArrowDown)panel.FindName("arrowDown");
arrowDown.TooltipPlacement = popup.VerticalOffset > 0
? PlacementMode.Bottom
: PlacementMode.Top;
};
}));
}
Now, with this approach, you should be able to use the ToolTipService attached properties too. So the following XAML would work.
<Button ToolTipService.ToolTip="!!!" Content="Hover Me" />
Hope this helps!
We figured out yesterday how to get a listbox's contents to switch between different panels I had stacked on top of each other. I'm attempting to do the same thing in WPF this time, obviously the syntax is different. The code worked 100% correctly in the windows form. I've tried a few different ways to try to get the now "grids" to show, but to no avail.
Thanks in advance!
Current code 'attempt'. I'm just demonstrating a couple ways I've attempted to change the code there in that first "case".
private void listBox1_SelectedIndexChanged(object sender, SelectionChangedEventArgs e)
{
// set the listboxselected item to a string variable
string curItem = listBox1.SelectedItem.ToString();
curItem = listBox1.SelectedItem.ToString();
// variable changes depening on mouse click, sets to whichever string value is selected
switch (curItem)
{
case "General":
gridGeneral.Visibility == true;
gridRightClick.Visibility = Visibility.Visible;
gridSnaps.Visibility = Visibility.Hidden;
break;
case "E-Snaps":
gridGeneral.Visibility = Visibility.Hidden;
gridRightClick.Visibility = Visibility.Hidden;
gridSnaps.Visibility = Visibility.Visible;
break;
case "Mouse":
gridGeneral.Visibility = Visibility.Hidden;
gridRightClick.Visibility = Visibility.Visible;
gridSnaps.Visibility = Visibility.Hidden;
break;
}
Here is the code that works in a windows form
private void listBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
// set the listboxselected item to a string variable
string curItem = listBox1.SelectedItem.ToString();
curItem = listBox1.SelectedItem.ToString();
// variable changes depening on mouse click, sets to whichever string value is selected
switch(curItem)
{
case "General" :
panel1.Visible = false;
panel2.Visible = true;
panel3.Visible = false;
panel4.Visible = false;
panel5.Visible = false;
break;
etc etc etc....
You wouldn't do this the same way in WPF that you would in WinForms
In WPF, you'll probably have a single control in WPF where in WinForms you have 3, and the Template that control uses to render will change based on the SelectedItem of your ListBox
Most likely the control definition will look something like this, so that the content of it is bound to the ListBox.SelectedItem:
<ContentControl Content="{Binding ElementName=listBox1, Path=SelectedItem}" />
And you can either use ContentTemplates or DataTemplates to tell WPF how to draw that ContentControl's ContentTemplate.
If the SelectedItem is a custom class, a DataTemplate would probably be easier, however since its a string in your example, a ContentTemplate is probably better.
Here's an example of a style for that ContentControl which changes the ContentTemplate property based on the value of the Content
<Style TargetType="{x:Type ContentControl}">
<!-- // Default Template -->
<Setter Property="ContentTemplate" Value="{StaticResource GeneralTemplate}" />
<!-- // Change template depending on a property -->
<Style.Triggers>
<Trigger Property="Content" Value="ESnaps">
<Setter Property="ContentTemplate" Value="{StaticResource ESnapsTemplate}" />
</Trigger>
<Trigger Property="Content" Value="Mouse">
<Setter Property="ContentTemplate" Value="{StaticResource MouseTemplate}" />
</Trigger>
</Style.Triggers>
</Style>
(I may have the syntax of the exact binding you need wrong here... will probably need some testing)
iam trying to create a TextBlock with runobject at runtime.
i need the textblock to show following text:
Please Click Here to go to next Page.
Click here should be a "link" like, where the user can click on it and he will move to the next tabitem ..
i have:
var tbGoToNextTab = new TextBlock
{
Content = "Please Click Here to go to next Page.",
Foreground = new SolidColorBrush(Colors.Black)
};
tbGoToNextTab .Click +=new RoutedEventHandler(tbGoToNextTab_Click);
how would i just make the Click Here Clickable, have underline and blue text color and perform an action when clicking on it ?
thanks in advance
EDIT:
I just want the 2 Words "Click Here" clickable ...the rest should be displayed as normal text..
I think i should be doing this with Inlines.. any suggestions ?
Something like this? (untested)
var hyperlink = new HyperLink(new Run("Click Here"));
hyperlink.Click += new RoutedEventHandler(tbGoToNextTab_Click);
var span = new Span();
span.Inlines.Add(new Run("Please "));
span.Inlines.Add(hyperlink);
span.Inlines.Add(new Run(" to go to next Page"));
var tbGoToNextTab = new TextBlock
{
Content = span,
Foreground = new SolidColorBrush(Colors.Black)
};
Ok found the Answer ...
As i supposed i had to use the Inline property ..
i found the answer in this post:
Add hyperlink to textblock wpf From Stanislav Kniazev
Thanks all...
To manage a left click action, add a handler on the PreviewMouseLeftButtonDown
tbGoToNextTab.PreviewMouseLeftButtonDown += OntbGoToNextTabPreviewMouseLeftButtonDown;
void OntbGoToNextTabPreviewMouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
// write code here...
}
To manage underline and color change, write a Style in xaml, in this Style put a Trigger that will change your TextBlock on the IsMouseOver property change.
<Window.Resources>
<Style x:Key="HyperLinkStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="TextDecorations" Value="Underline" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
tbGoToNextTab.Style = FindResource("HyperLinkStyle") as Style;
You will have to declare a TextBlock specifically for the clickable text and other TextBlocks for the static text.
Have something like this..
Textblock as content in the button..so that its clickable.
Canvas pnel = new Canvas();
var btntab = new Button();
var tbGoToNextTab1 = new TextBlock();
var tbGoToNextTab2 = new TextBlock();
var tbGoToNextTab3 = new TextBlock();
tbGoToNextTab1.Text = "Please ";
tbGoToNextTab2.Text = "Click Here";
tbGoToNextTab3.Text = " to go to next Page.";
tbGoToNextTab1.Margin = new Thickness(0, 0, 0, 0);
btntab.Margin = new Thickness(40, 0, 0, 0);
tbGoToNextTab3.Margin = new Thickness(95, 0, 0, 0);
tbGoToNextTab1.Foreground = new SolidColorBrush(Colors.Black);
tbGoToNextTab2.Foreground = new SolidColorBrush(Colors.Black);
tbGoToNextTab3.Foreground = new SolidColorBrush(Colors.Black);
btntab.Click += new RoutedEventHandler(btntab_Click);
btntab.Content = tbGoToNextTab2;
pnel.Children.Add(tbGoToNextTab1);
pnel.Children.Add(btntab);
pnel.Children.Add(tbGoToNextTab3);
Text112.Children.Add(pnel);
If you handle Click event for the TextBlock, it will process click anywhere within the boundary of the control.
If you need only part of the text clickable, you should place a Hyperlink within the body of your TextBlock. By the way, it has an extra benefit of highlighting the clickable area so that your was not confused with the text that says, "Click Here" which is not clickable.
Here's relevant WPF snippet:
<TextBlock>
<Run>Please</Run>
<Hyperlink>
<Run Text="Click Here"/>
</Hyperlink>
<Run>to go to next Page.</Run>
</TextBlock>