TextBlock MouseDown URL - How get my url in my code behind? - wpf

I ve a list from sharepoint and i collect from this list an hyperlink.
As i want my textbox to be like an hyperlink I ve added an event on mousedown to open this hyperlink, My concern is how to collect this hyperlink in the codebehind with the sender.
For the moment I've just hide this hyperlink in the tooltip maybe i can manage this differently any suggestion will be grantly appreciated.
My point so far, i don't know how to get this tooltip in the code behind.
Thanks
My XAML Code :
<ListBox Name="ListboxTips" ItemsSource="{Binding}" ScrollViewer.HorizontalScrollBarVisibility="Disabled">
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding Path=Picture}" Height="20"></Image>
<TextBlock MouseDown="TextBlock_MouseDown_URL" TextDecorations="Underline"
Margin="10,10,20,10" Width="160" TextWrapping="Wrap"
Text="{Binding Path=TitleTip}"
ToolTip="{Binding Path=URL}"/>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
My code behind :
foreach (SPSClient.ListItem item in TipsList)
{
var tips = new Tips();
tips.TitleTip = item.FieldValues.Values.ElementAt(1).ToString();
tips.App = item.FieldValues.Values.ElementAt(4).ToString();
// get the Hyperlink field URL value
tips.URL = ((FieldUrlValue)(item["LinkDoc"])).Url.ToString();
//should collect the description of the url
//tips.URLdesc = ((FieldUrlValue)(item["LinkDoc"])).Description.ToString();
tips.Picture = item.FieldValues.Values.ElementAt(4).ToString();
colTips.Add(tips);
}
ListboxTips.DataContext = colTips;
....
private void TextBlock_MouseDown_URL(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
//string test = (ToolTip)(sender as Control).ToString();
System.Diagnostics.Process.Start("http://www.link.com");
//System.Diagnostics.Process.Start(test);
}
Thanks a lot,

You can just access the property directly. It is not elegant, but will work!
private void TextBlock_MouseDown_URL(object sender, MouseButtonEventArgs e)
{
TextBlock txtBlock = sender as TexBlock;
// just access the property
string url = txtBlock.ToolTip as string;
}
A more elegant approach might be to use a Button, Hyperlink or something that exposes a Command, so that you can bind the 'click' action to a command on your view model that performs the action you wish to execute.

usually you stick any data you want to trespass somewhere to Tag attribute.
<TextBlock .. Tag="{Binding Path=URL}" />
This is easily retrievable as a public property:
private void TextBlock_MouseDown_URL(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var tb = sender as TextBlock;
if(tb != null)
{
var neededUrl = tb.Tag;
}
}

Related

How to change collection images in silverlight?

My Design Code like this:
<Grid HorizontalAlignment="Left" Height="42" VerticalAlignment="Top" Width="302" Margin="12,471,0,0" Background="{StaticResource AppBarItemForegroundThemeBrush}">
<TextBlock HorizontalAlignment="Left" Margin="6,6,0,0" TextWrapping="Wrap" Text="Change Color" VerticalAlignment="Top" Height="26" Width="137" FontSize="18" Foreground="Black" />
<Image HorizontalAlignment="Left" Height="33" Margin="163,3,0,0" VerticalAlignment="Top" Width="41" Source="Assets/c1-1.png" x:Name="c1" Tapped="c1_Tapped" />
<Image HorizontalAlignment="Left" Height="32" Margin="212,4,0,0" VerticalAlignment="Top" Width="45" Source="Assets/c3-1.png" x:Name="c2" Tapped="c2_Tapped" RenderTransformOrigin="0.825,0.5" />
<Image HorizontalAlignment="Left" Height="33" Margin="262,3,0,0" VerticalAlignment="Top" Width="40" Source="Assets/c2-1.png" x:Name="c3" Tapped="c3_Tapped" />
</Grid>
Code Behind code like this:
private void c1_Tapped(object sender, TappedRoutedEventArgs e)
{
Images = new ObservableCollection<string>();
Images.Add(#"Assets/02_perspective_img_1.png");
Images.Add(#"Assets/02_perspective_img_2.png");
Images.Add(#"Assets/02_perspective_img_3.png");
this.DataContext = this;
}
private void c2_Tapped(object sender, TappedRoutedEventArgs e)
{
Images = new ObservableCollection<string>();
Images.Add(#"Assets/03_perspective_img_1.png");
Images.Add(#"Assets/03_perspective_img_2.png");
Images.Add(#"Assets/03_perspective_img_3.png");
this.DataContext = this;
}
private void c3_Tapped(object sender, TappedRoutedEventArgs e)
{
Images = new ObservableCollection<string>();
Images.Add(#"Assets/01_perspective_img_1.png");
Images.Add(#"Assets/01_perspective_img_2.png");
Images.Add(#"Assets/01_perspective_img_3.png");
this.DataContext = this;
}
When tapped on particular image need to show that particular images.But not showing that .
only showing first clicked items images only.Please let me know how to change the collection.
i am binding that collection to flipview control in windows 8.
<FlipView.ItemTemplate>
<DataTemplate>
<Image HorizontalAlignment="Left" Source="{Binding}" Height="450" VerticalAlignment="Top" Width="792" x:Name="imagecontrol" Stretch="Fill"/>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
I'm going under a few assumptions. One is that the FlipView control has it's ItemsSource binding to your Images property. If you are going to set the DataContext to yourself (the page in question) you need to do one of a few options.
One: Do not set Images to a new collection. You are using an ObservableCollection so take advantage of it. Clear the collection and add items back to it.
private void c3_Tapped(object sender, TappedRoutedEventArgs e)
{
Images.Clear();
Images.Add(#"Assets/01_perspective_img_1.png");
Images.Add(#"Assets/01_perspective_img_2.png");
Images.Add(#"Assets/01_perspective_img_3.png");
}
Two: Implement INotifyPropertyChanged in the page and fire the PropertyChanged event when you reset the Images property
private ICollection<string> _images;
public ICollection<string> Images
{
get { return _images; }
set
{
_images = value;
OnPropertyChanged("Images");
}
}
You will probably find that you will need more and more bindings. Because of this it is usually best to have a separate ViewModel class that holds your data.
the way Shawn is suggested is i will recommend but when i tested it in case of Windows 8 app page which is inherited by LayoutAwarePage then in that case - Firstly you got error that Page Can not Implement InotifypropertyChanged secondly when i Tested it With ObsevableCollection that time also it is not working..so i have come with this workaround for solving your problem. in this case you just have to update the itemsSource Property of your FlipView Control each time when you are updating your Image collection..
private void c1_Tapped(object sender, TappedRoutedEventArgs e)
{
Images = new ObservableCollection<string>();
Images.Add(#"Assets/02_perspective_img_1.png");
Images.Add(#"Assets/02_perspective_img_2.png");
Images.Add(#"Assets/02_perspective_img_3.png");
FlipviewControlName.ItemsSource = Images;
}
I know this is not the proper solution but i think i will solve your problem...

Binding TextBlock based on selection in ComboBox

When I make selection in ComboBox, and then type some text in TextBox, I want to have visible AutoSuggestion list of ID or FirstName or LastName (based on ComboBox Selection) that contains typed string in TextBox. Like this, now it works only for FirstName.
I have problem to somehow set dynamically binding for TextBlock.
Please Help.
Thanks in advance! Marina
I have ComboBox:
<ComboBox Height="23" Name="cbAttrib" Width="120" Margin="0,8,0,0">
<ComboBoxItem>ID</ComboBoxItem>
<ComboBoxItem>FirstName</ComboBoxItem>
<ComboBoxItem>LastName</ComboBoxItem>
</ComboBox>
I have TextBox:
<TextBox Name="txtSearch" TextChanged="txtAutoSuggestName_TextChanged"/>
And this ListBox:
<ListBox Name="listBoxSuggestion" Visibility="Hidden" SelectionChanged="ListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock DataContext="{Binding FirstName}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and in code I have this methods:
private void txtAutoSuggestName_TextChanged(object sender, TextChangedEventArgs e)
{
listBoxSuggestion.Items.Clear();
if (txtSearch.Text != "")
{
ComboBoxItem cb = (ComboBoxItem)cbAttrib.SelectedItem;
Collection<Person> namelist = proxy.PersonSearch(txtSearch.Text, cb.Content.ToString());
if (namelist.Count > 0)
{
listBoxSuggestion.Visibility = Visibility.Visible;
foreach (var obj in namelist)
{
listBoxSuggestion.Items.Add(obj);
}
}
}
else
{
listBoxSuggestion.Visibility = Visibility.Hidden;
}
}
private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0)
{
txtSearch.Text = (e.AddedItems[0] as Person).FirstName.ToString();
listBoxSuggestion.Visibility = System.Windows.Visibility.Hidden;
}
}
You are not binding the Text so nothing will display
You just bind the DataContext, which does nothing if there are no additional bindings which will be relative to it. Just swap that (or add Text="{Binding}" which will bind to the DataContext which is the FirstName) and if your logic is correct it should work.
(Instead of clearing and adding to Items you should just set the ItemsSource instead. listBoxSuggestion.ItemsSource = namelist;)
Edit: To make the binding work for different suggestions change the binding path to Value and make the ItemsSource a collection of some simple objects with a Value property (e.g. use LINQ and anonymous objects).

How to Identify button for list item

How do i access the object UserNames, that is bound to the list??
What i did so far:
Item of the list is object in my case:
new List<UserNames>();
this.users.Add(new UserNames() {Id = 1, UserName = "name 1"});
I am using data template for which i have label and button.
My List is as follows:
<ListBox Grid.Column="1" Grid.Row="1" Name="listBox1" ItemsSource="{Binding}" SelectedValuePath="Id">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel Orientation="Vertical">
<StackPanel>
<Label Content="{Binding UserName}" />
</StackPanel>
<StackPanel Name="ButtonStackPanel">
<Button Name="MyButton" Content="Click Me" Click="MyButton_Click">
</Button>
</StackPanel>
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Where my method for Button is. As you can see i did try to utilise the parent option, but without sucess
private void MyButton_Click(object sender, RoutedEventArgs e)
{
//StackPanel panel = (StackPanel)((Button)sender).Parent;
//WrapPanel wrapPanel = (WrapPanel) panel.Parent;
//ListItem listItem = (ListItem) wrapPanel.Parent;
//ListBox box = (ListBox) listItem.Parent;
//UserNames itemToReport = (UserNames) (box.SelectedItem);
//MessageBox.Show(itemToReport.UserName);
}
You can use the Button's DataContext, since it will be your UserName object
private void MyButton_Click(object sender, RoutedEventArgs e)
{
Button b = sender as Button;
UserNames data = b.DataContext as UserNames;
MessageBox.Show(data.UserName);
}
I've always thought that with WPF, your application is the DataContext, while the UI objects like Buttons, ListBoxes, TextBoxes, etc are simply a pretty layer that sits on top of the DataContext to allow the User to interact with it.
In the XAML, set the Tag property to the current item.
In the click handler, cast it back.
Usernames user = (sender as Button).Tag as Usernames;
To bind a datacollection it is often easiest to use an ObservableCollection (if the data is changing runtime). When you do the binding you have to define a datacontext, a datasoure and a datapath. I will advice you to read some more about binding on MSDN :D
This will work for you -
MessageBox.Show(((sender as Button).DataContext as UserNames).UserName);

How to update Source property of a frame by clicking only one button from user control?

I’d like to find out about how to update a source property by clicking only one "Next" button based on a click count and being able to load different pages into frame each time the button is clicked another time. Any advice is highly appreciated! Thank you in advance.
Main Window Code:
<Grid x:Name="LayoutRoot">
<Frame Content="Frame" Source="/WpfApplication1;component/Page1.xaml"/>
<local:NavUserControl HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
</Grid>
User control that contains the button:
<StackPanel x:Name="LayoutRoot" Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="0,0,0,20">
<Button Content="Back" HorizontalAlignment="Left" Width="75"/>
<Button Content="Next" HorizontalAlignment="Left" Width="75" />
</StackPanel>
Create a PageViewModel class that implements NextPageCommand and PreviousPageCommand commands, which raise (respectively) UserNavigatedToNextPage and UserNavigatedToPreviousPage events. To make it simple, also have them expose NextPage and PreviousPage properties of type PageViewModel. Create subclasses of PageViewModel for each page.
Create a view model class for the owning UserControl that exposes a CurrentPage property of type PageViewModel. Create all of the PageViewModel objects and set NextPage and PreviousPage on each. Add handlers for the navigation events on these object that look something like:
public void Page_UserNavigatedToNextPage(object sender, EventArgs e)
{
if (sender == CurrentPage && CurrentPage.NextPage != null)
{
CurrentPage = CurrentPage.NextPage;
}
}
Assuming that you've implemented property-change notification, now whenever the current page's NextPageCommand or PreviousPageCommand executes, the CurrentPage property will be updated and will be reflected in the UI. If you've created a data template for each page view model type, all you need is
<ContentPresenter Content="{Binding CurrentPage}"/>
in your user control and you're good to go.
If the Next/Previous buttons are in your control, and not in the page, then implement properties in the main view model that expose CurrentPage.NextPageCommand and CurrentPage.PreviousPageCommand, and bind the buttons to them.
In your NavUserControl, I would wire up either events or commands (or both, perhaps) for the next and back buttons. Then you can access those from within the MainWindow and set the appropriate value into the Source property.
If you go the event route, attach onto the events and set the Source directly.
If you go the command route, setup a command in your viewmodel, bind it to the usercontrol, and bind the Source property to another value in your viewmodel.
Edit: Adding some code per the OP's request. Keep in mind, this is not intended to be best practices. Just some examples.
To go the event route should be the simplest. You already know how to do this, I'd imagine. Just add:
public event EventHandler BackClicked;
public event EventHandler NextClicked;
private void Back_Click(object sender, RoutedEventArgs e)
{
BackClicked(sender, e);
}
private void Next_Click(object sender, RoutedEventArgs e)
{
NextClicked(sender, e);
}
events to your NavUserControl. Then change your XAML to:
<StackPanel x:Name="LayoutRoot" Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="0,0,0,20">
<Button Content="Back" HorizontalAlignment="Left" Width="75" Click="Back_Click" />
<Button Content="Next" HorizontalAlignment="Left" Width="75" Click="Next_Click" />
</StackPanel>
And now in your MainWindow.xaml.cs file, add:
private void BackClicked(object sender, EventArgs e)
{
Uri source = // Whatever your business logic is to determine the previous page;
_Frame.Source = source;
}
private void NextClicked(object sender, EventArgs e)
{
Uri source = // Whatever your business logic is to determine the next page;
_Frame.Source = source;
}
and change the MainWindow XAML to be:
<Grid x:Name="LayoutRoot">
<Frame x:Name="_Frame" Content="Frame"
Source="/WpfApplication1;component/Page1.xaml"/>
<local:NavUserControl HorizontalAlignment="Center" VerticalAlignment="Bottom"
BackClicked="BackClicked" NextClicked="NextClicked" />
</Grid>
Going the command route takes a little more architecting, but is a lot more clean. I'd recommend using your favorite MVVM toolkit. My favorite is MVVMLight, so that's what I'll use for this example.
Create a ViewModel class, something like this:
public class ViewModel : GalaSoft.MvvmLight.ViewModelBase
{
private Uri _Source;
public Uri Source
{
get { return _Source; }
set
{
if (_Source != value)
{
_Source = value;
RaisePropertyChanged("Source");
}
}
}
private GalaSoft.MvvmLight.Command.RelayCommand _BackCommand;
public ICommand BackCommand
{
get
{
if (_BackCommand == null)
{
_BackCommand = new GalaSoft.MvvmLight.Command.RelayCommand(() =>
{
Uri source = // Whatever your business logic is to determine the previous page
Source = source;
});
}
return _BackCommand;
}
}
private GalaSoft.MvvmLight.Command.RelayCommand _NextCommand;
public ICommand NextCommand
{
get
{
if (_NextCommand == null)
{
_NextCommand = new GalaSoft.MvvmLight.Command.RelayCommand(() =>
{
Uri source = // Whatever your business logic is to determine the next page
Source = source;
});
}
return _NextCommand;
}
}
}
In your MainWindow.xaml.cs, create an instance of this class and set your DataContext property to that instance. Then setup your bindings:
<Grid x:Name="LayoutRoot">
<Frame Content="Frame" Source="{Binding Source}"/>
<local:NavUserControl HorizontalAlignment="Center" VerticalAlignment="Bottom"/>
</Grid>
and
<StackPanel x:Name="LayoutRoot" Orientation="Horizontal" VerticalAlignment="Bottom" HorizontalAlignment="Center" Margin="0,0,0,20">
<Button Content="Back" HorizontalAlignment="Left" Width="75" Command="{Binding BackCommand}"/>
<Button Content="Next" HorizontalAlignment="Left" Width="75" Command="{Binding NextCommand}" />
</StackPanel>
The binding example is pretty straight-forward MVVM-style WPF. I'd suggest you go that route and if you need more help, go read up on MVVM in WPF. Lots of resources out there in the form of tutorials and books. Searching here on SO can help a lot as well.
Edit again:
Change your constructor to this:
public MainWindow()
{
this.InitializeComponent();
// Insert code required on object creation below this point.
DataContext = new ViewModel();
}

Xaml Binding outside itemtemplate

I would like to bind data of my listbox. Imagine I have something like :
<ListBox ItemsSource="{Binding MyList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text={Binding Value} />
<TextBlock Text={Binding AbsoluteValue} />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
MyList contains an observable collection of an object that has a property named "Value"
AbsoluteValue is a property of the view model, as the MyList property.
Of course, the seconde textbox will have always the same value, but it is what I want :)
How can I tell the binding that the datacontext is not the same for the second textbox ?
Thanks in advance for any help
EDIT : my real sample is a StackPanel.
I've tryed
private void StackPanel_Loaded(object sender, RoutedEventArgs e)
{
StackPanel stackPanel = sender as StackPanel;
stackPanel.SetBinding(StackPanel.VisibilityProperty, new Binding("Loaded") { Source = DataContext, Mode = BindingMode.TwoWay });
}
but it's not working
XAML:
<TextBlock x:Name="tbAbsoluteValue" Loaded="AbsoluteValue_Loaded" />
Codebehind:
void AbsoluteValue_Loaded(object sender, RoutedEventArgs e)
{
TextBlock absoluteValue = sender as TextBlock;
absoluteValue.SetBinding(TextBlock.TextProperty, new Binding("AbsoluteValue") { Source = VIEW_MODEL_OBJECT, Mode = BindingMode.TwoWay });
}
That's one way to achieve what you want, you could also use a converter too, or create a StaticResource in your Resources for the VM and bind to that as a source.

Resources