Finding a control inside another control in WPF - c#

Since there is no link button in WPF I created a link button using hyperlink and text block controls.
There are 3 controls:
<TextBlock Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Left" >
<Hyperlink Name="hyplnkIsActiveMarkets" Click="hyplnkIsActive_Click" Foreground="Blue" >
<TextBlock Name="txtblkIsActiveMarkets" Text="Active" />
</Hyperlink>
</TextBlock>
<TextBlock Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left">
<Hyperlink Name="hyplnkIsActiveBudgets" Click="hyplnkIsActive_Click" Foreground="Blue" >
<TextBlock Name="txtblkIsActiveBudgets" Text="Active" />
</Hyperlink>
</TextBlock>
<TextBlock Grid.Column="2" VerticalAlignment="Center" HorizontalAlignment="Left">
<Hyperlink Name="hyplnkIsActivePrograms" Click="hyplnkIsActive_Click" Foreground="Blue" >
<TextBlock Name="txtblkIsActivePrograms" Text="Active" />
</Hyperlink>
</TextBlock>
All the link buttons calls same click method
private void hyplnkIsActive_Click(object sender, RoutedEventArgs e)
{
Hyperlink objHyperlink = (Hyperlink)sender;
TextBlock objTextBlock = new TextBlock();
if (objHyperlink == hyplnkIsActiveMarkets)
{
objTextBlock = txtblkIsActiveMarkets;
}
else if (objHyperlink == hyplnkIsActiveBudgets)
{
objTextBlock = txtblkIsActiveBudgets;
}
else if (objHyperlink == hyplnkIsActivePrograms)
{
objTextBlock = txtblkIsActivePrograms;
}
if (objTextBlock.Text == "Active")
ChangeHyperLinkStatus(objHyperlink, objTextBlock, Status.Inactive);
else ChangeHyperLinkStatus(objHyperlink, objTextBlock, Status.Active);
}
In the click method I check for the text block inside the hyper link individually using if condition.
Is there any easier way to do this? That's basically finding control inside a control?

UPDATE: you can not use VisualTreeHelper.GetParent(...) to get the parent of your hyperlink as you have mentioned hyperlink is not visual. corrected the answer.
See code below.
private void hyplnkIsActive_Click(object sender, RoutedEventArgs e)
{
Hyperlink objHyperlink = (Hyperlink)sender;
TextBlock objTextBlock = (TextBlock)LogicalTreeHelper.GetChildren(objHyperlink)[0];
// This will give logical tree first child of objHyperlink
if (objTextBlock.Text == "Active")
ChangeHyperLinkStatus(objHyperlink, objTextBlock, Status.Inactive);
else
ChangeHyperLinkStatus(objHyperlink, objTextBlock, Status.Active);
}
See this article about logical tree on MSDN

I think you're going into the wrong direction by relaying your execution logic on controls and not on data.
You can, for example, bind a ICommand or RelayCommand to the buttons, or just subscribe different events, or define a custom DataTemplate where on mouse down the clicked control is assignable to some ModelView property.
Doing in way you do, you create tough coupling between UI and your execution logic.
In this case easier use WindowsForm then WPF.

I got it finally . Thanks to Maheep fa his help
TextBlock objTextBlock = (TextBlock)LogicalTreeHelper.GetChildren(objHyperlink).Cast<System.Windows.Documents.InlineUIContainer>().FirstOrDefault().Child;

Related

Dynamic tooltip with textblock in WPF

i'm have some trouble with tooltip in textblock wpf .
After i completed 1 task and if task is error i want update error info with tooltip . But tooltip never show when completed . Please help me .
Thanks
Here my code
C# code
if (status == "Error")
{
LogCreateSite item = (LogCreateSite)gridLog.Items[rowIndex];
item.ErrorInfo = "Error";
DataTemplate template = cellTitle.ContentTemplate;
Canvas canvas = (Canvas)template.LoadContent();
TextBlock txtError = (TextBlock)canvas.Children[1];
ToolTip toolTip = new ToolTip();
toolTip.Content = "asdfasdf";
txtError.ToolTip = toolTip;
txtError.UpdateLayout();
}
And my Xaml :
<DataTemplate x:Key="error">
<Canvas Margin="10,15,0,0">
<!--<Ellipse Fill="#FF5050" Width="12" Height="12">
</Ellipse>-->
<Viewbox Width="16" Height="16">
<Frame Source="../Icon/Error_16.xaml" />
</Viewbox>
<TextBlock Text="Error" Margin="25,-3,0,0">
</TextBlock>
<TextBlock Cursor="Hand" Name="txtErrorInfo" ToolTip="{Binding ErrorInfo, Mode=OneWay,UpdateSourceTrigger=PropertyChanged}" FontSize="14" Text="?" Margin="60,-3,0,0" FontWeight="Bold" Foreground="Blue">
</TextBlock>
</Canvas>
</DataTemplate>
You need to do binding for tool tip to show the error/message to user.
Please go thru this tutorial for wpf binding. Introduction to WPF data binding from WPF-tutorial.
your XAML should be like this for a proper binding.
Name="txtErrorInfo" ToolTip="{binding path=error mode=OneWay UpdateSourceTrigger=PropertyChanged}"
It is required to mention mode and UpdateSourceTrigger when you are changing the property which should be shown to user.
Correcting your code, see this sample for how to show ToolTip from code :
private void Button_Click_1(object sender, RoutedEventArgs e)
{
ToolTip t = new ToolTip();
t.Content = DateTime.Now.ToString();
t.IsOpen = true;
t.PlacementTarget = txtError;
t.Placement = System.Windows.Controls.Primitives.PlacementMode.Bottom;
txtError.ToolTip = t;
}

Hyperlink click action and ability to select and copy text

I'm working on a WPF application in C# and need the ability for users to click a hyperlink(execute the command) but also have the ability to select the text and copy it.
I searched for options but could not find anything that would help me.
Currently i have the following in my WPF XAML:
<TextBlock Grid.Column="1" Grid.Row="0">
<Hyperlink Command="{Binding OpenDefaultMailApplicationCommand}" >
<TextBox Height="20" IsReadOnly="True" Foreground="Blue" BorderThickness="0">test#test.nl</TextBox>
</Hyperlink>
</TextBlock>
What am i doing wrong? The text is selectable only i don't have the ability to click on it to execute my command.
This is what I use for similar req.
You need to have a textbox, which is non editable and is clickable. you can style it as a hyperlink by making in underlined and color changes etc.
<TextBox IsReadOnly="True"
Background="Transparent"
BorderThickness="0"
Text="test#test.nl"
Height="20"
PreviewMouseLeftButtonDown="TextBox_PreviewMouseLeftButtonDown"
TextDecorations="Underline"
MouseMove="TextBox_MouseMove"
Foreground="Blue" />
private void TextBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (Keyboard.IsKeyDown(Key.LeftCtrl))
{
MessageBox.Show("CLicked");
}
}
private void TextBox_MouseMove(object sender, MouseEventArgs e)
{
var txtBox = sender as TextBox;
if (Keyboard.IsKeyDown(Key.LeftCtrl))
{
txtBox.Cursor = Cursors.Hand;
}
else
txtBox .Cursor = null;
}
So i found a other approach for the problem.
By using a richtextbox and adding a hyperlink to that, a user is able to select the text but can also click it.
<RichTextBox Name="rtbEmail" Grid.Column="1" Grid.Row="0" Foreground="Blue" BorderThickness="0"
Margin="3"/>
In the 'C#' usercontrol.cs i programmaticly set the hyperlink and add a click eventhandler to it.
FlowDocument doc = new FlowDocument();
rtbEmail.Document = doc;
rtbEmail.IsReadOnly = true;
rtbEmail.IsDocumentEnabled = true;
Paragraph para = new Paragraph();
doc.Blocks.Add(para);
Hyperlink link = new Hyperlink();
link.IsEnabled = true;
link.Inlines.Add(DataContext.EmailAddress);
link.Click += new RoutedEventHandler(this.OpenEmailAppEvent);
para.Inlines.Add(link);
Hopefully someone has use for this :). i did :)

How to change the foreground of a specific word in a textblock?

I've got a textblock in which the user sees a stacktrace, like this:
System.ArgOutOfRangeExc.:
Argument is out of range.
Parametername: index
at System.Collections.ArrayList.getItem(Int32 index)
//...
at SomethingElse.formData.formData_CloseForm(Object sender, FormClosingEventArgs e)
The idea is, that everything like "System...." gets colored grey and the rest of the stacktrace (here: "at SomethingElse....") should not be colored.
I dont know how and where to start and how to manage this problem. Any solutions? I'm working with C# and WPF
EDIT: The text in the textbox is not static. The text changes everytime the user clicks on a row in a DataGrid, so I need to do that programmatically (working with Substring will get very complicated)
You can simply use a number of Run elements inside your TextBlock. Each Run can have it's own formatting. Take this simple example:
<TextBlock FontSize="14" Margin="20">
<Run Text="This is Green," Foreground="Green" />
<Run Text="this is Red" Foreground="Red" />
<Run Text="and this is Blue AND Bold" Foreground="Blue" FontWeight="Bold" />
</TextBlock>
Please note that the Run.Text property is a DependencyProperty, so you can also data bind its value. This can also be accomplished programmatically:
<TextBlock Name="TextBlock" FontSize="14" Margin="20" />
...
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Run run = new Run("This is Green,");
run.Foreground = Brushes.Green;
TextBlock.Inlines.Add(run);
run = new Run(" this is Red");
run.Foreground = Brushes.Red;
TextBlock.Inlines.Add(run);
run = new Run(" and this is Blue AND Bold");
run.Foreground = Brushes.Blue;
run.FontWeight = FontWeights.Bold;
TextBlock.Inlines.Add(run);
}

WPF: Easiest way to add control to grid based on ListView selection

I have a ListView which contains items that should represent certain settings, which can be changed by the user. I am looking for the easiest way to link the ListViewItem to a custom user control.
What I have now:
<ListView
Grid.Row="0"
Grid.Column="0"
SelectionChanged="SettingsListViewSelectionChanged">
<ListViewItem x:Name="PathSettings" Content="Path"/>
<ListViewItem x:Name="HideShowTvShows" Content="Hide/Show TV Shows"/>
</ListView>
And then in the code behind I figure out which item was clicked, and attach the corresponding UserControl to the Grid.
private void SettingsListViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var listView = e.Source as ListView;
if (listView != null)
{
SettingsContentPanel.Children.Clear();
if (listView.SelectedItem.Equals(_pathSettings))
{
SettingsContentPanel.Children.Add(_pathSettings);
_pathSettings.SetValue(Grid.RowProperty, 0);
_pathSettings.SetValue(Grid.ColumnProperty, 1);
}
if (listView.SelectedItem.Equals(_hideShowTvShowsSettings))
{
SettingsContentPanel.Children.Add(_hideShowTvShowsSettings);
_hideShowTvShowsSettings.SetValue(Grid.RowProperty, 0);
_hideShowTvShowsSettings.SetValue(Grid.ColumnProperty, 1);
}
}
}
And the Grid itself:
<Grid
x:Name="SettingsContentPanel"
Grid.Row="0"
Grid.Column="2"
Grid.ColumnSpan="2" />
Is there a way to get rid of the boiler plate code behind and use XAML for this purpose?
Looks like you're looking for a ContentPresenter:
<ContentPresenter x:Name="SettingsContentPanel"
Content="{Binding SelectedItem, ElementName=MyLstView}"/>

WPF, sibling selection like jQuery?

I have the following XAML code as an example in my WPF application
<StackPanel Height="23" Name="MSpanel" Orientation="Horizontal" Width="138" Margin="37,13,0,0" VerticalAlignment="Top" HorizontalAlignment="Left" >
<TextBox Height="23" Name="MTBox" Width="120" Text="0" />
<ScrollBar Height="23" Name="MSBar" Width="18" TouchUp="SBar_TouchUp" />
</StackPanel>
<StackPanel Height="23" Name="CSPanel" Orientation="Horizontal" Width="138" Margin="37,41,0,0" VerticalAlignment="Top" HorizontalAlignment="Left">
<TextBox Height="23" Name="CTBox" Width="120" Text="0" />
<ScrollBar Height="23" Name="CSBar" Width="18" TouchUp="SBar_TouchUp" />
</StackPanel>
I have this function:
private void SBar_TouchUp(object sender, TouchEventArgs e)
{
//what goes here?
//siblings.getFirst('textbox').text += 1;
}
What I was hoping to do, is have 1 function that controls these "Psudo" numeric up downs in WPF. If there was some way to have a unified function that could, reference the sibling textbox, so I only have to write it once. That would be ideal.
I'm very familiar with jQuery, and XAML looks like an HTML DOM, ... Is there a way to browse the tree?
I realize there are existing Numeric Up Downs available to download. This idea I believe would be good to know for the future in other endeavors as well. Thanks.
The solution that worked!
private void SBar_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if (e.NewValue == 0) return; //abort here, no change
ScrollBar sb = (ScrollBar)sender;
StackPanel sp = (StackPanel)sb.Parent;
TextBox tb = (TextBox)sp.Children[0];
int change = e.NewValue < 0 ? 1 : -1;
sb.Value = 0; //this will invoke this function again
tb.Text = (Convert.ToInt32(tb.Text) + change).ToString();
}
Each element in the visual tree has a Parent and VisualParent property - as all elements are based on UIElement - either should give you the parent object.
In this case the parent of the ScrollBar is the StackPanel. You can then use the Children property of the StackPanel to get the collection of child objects. You know which is the ScrollBar (it's the sender) so the other must be the TextBox.
You can do something like this:
private void SBar_TouchUp(object sender, TouchEventArgs e)
{
//siblings.getFirst('textbox').text += 1;
var siblings = ((sender as FrameworkElement).Parent as Panel).Children;
var textbox = siblings.OfType<TextBox>().First();
textbox.Text = (int.Parse(textbox.Text) + 1).ToString();
}
but I would suspect that there are probably better ways to do what you want, like data binding or naming elements in attached properties.
Yeah, there are many properties for both Logical and Visual trees.
Like FrameworkElement.Parent or Panel.Children.
I don't think there is directly method to get sibling, but its not that hard to get index in list of children of parent and getting next item.

Categories