My problem is to find the urls from the text content and convert it into the clickable hyperlinks via data binding.
This is what I've tried
<TextBlock Tag="{Binding message}" x:Name="postDescription" TextWrapping="Wrap"
Grid.Row="3" Grid.ColumnSpan="3" Margin="10,10,10,12" FontSize="16"
TextAlignment="Justify" Foreground="{StaticResource foreGroundWhite}" >
<Run Text="{Binding description, Converter={StaticResource statusFormatter}}" />
</TextBlock>
In code,
public class StatusFormatter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
return returnTextWithUrl((String)value);
}
public static String returnTextWithUrl(String text)
{
if(text == null) { return null; }
MatchCollection mactches = uriFindRegex.Matches(text);
foreach (Match match in mactches)
{
//Need Help here
HyperlinkButton hyperlink = new HyperlinkButton();
hyperlink.Content = match.Value;
hyperlink.NavigateUri = new Uri(match.Value);
text = text.Replace(match.Value, ??);
}
return text;
}
}
}
The output should be something like this
<TextBlock Tag="{Binding message}" x:Name="postDescription" TextWrapping="Wrap"
Grid.Row="3" Grid.ColumnSpan="3" Margin="10,10,10,12" FontSize="16"
TextAlignment="Justify" Foreground="{StaticResource foreGroundWhite}" >
Click this link -
<Hyperlink NavigateUri="http://www.bing.com">bing</Hyperlink>
- for more info.
</TextBlock>
Any Help?
To do what you want you will have to use Inlines property of your TextBlock, but as it's not a DependencyProperty, it cannot be a target of binding. We will have to extend your TextBlock class, but as it's sealed we will have to use other class.
Lets define static class, which will add apropriate Inline - Hyperlink or Run, depending on Regex match. It can look for example like this:
public static class TextBlockExtension
{
public static string GetFormattedText(DependencyObject obj)
{ return (string)obj.GetValue(FormattedTextProperty); }
public static void SetFormattedText(DependencyObject obj, string value)
{ obj.SetValue(FormattedTextProperty, value); }
public static readonly DependencyProperty FormattedTextProperty =
DependencyProperty.Register("FormattedText", typeof(string), typeof(TextBlockExtension),
new PropertyMetadata(string.Empty, (sender, e) =>
{
string text = e.NewValue as string;
var textBl = sender as TextBlock;
if (textBl != null)
{
textBl.Inlines.Clear();
Regex regx = new Regex(#"(http://[^\s]+)", RegexOptions.IgnoreCase);
var str = regx.Split(text);
for (int i = 0; i < str.Length; i++)
if (i % 2 == 0)
textBl.Inlines.Add(new Run { Text = str[i] });
else
{
Hyperlink link = new Hyperlink { NavigateUri = new Uri(str[i]), Foreground = Application.Current.Resources["PhoneAccentBrush"] as SolidColorBrush };
link.Inlines.Add(new Run { Text = str[i] });
textBl.Inlines.Add(link);
}
}
}));
}
Then in XAML we use it just like this:
<TextBlock local:TextBlockExtension.FormattedText="{Binding MyText}" FontSize="15"/>
And after putting some text to my property:
private void firstBtn_Click(object sender, RoutedEventArgs e)
{
MyText = #"Simple text with http://mywebsite.com link";
}
I can see such a result:
I stumbled on this post while looking for the same for UWP. In case you are here too for the same, I'd recommend you use a HyperlinkButton instead of a Hyperlink wrapped in a Textblock. Below is the code on how to use it.
<HyperlinkButton Content="{x:Bind Text}" NavigateUri="{x:Bind Hyperlink}"/>
You can also use Binding instead of x:Bind and yes you can set the Mode=OneWay too.
Read More on Microsoft Docs
You can't put Hyperlink objects inside a String. Instead you need to return a Span containing inlines from your converter. The plain text will be Run objects and the links will be Hyperlink objects.
public static Span returnTextWithUrl(String text)
{
if(text == null) { return null; }
var span = new Span();
MatchCollection mactches = uriFindRegex.Matches(text);
int lastIndex = 0;
foreach (Match match in mactches)
{
var run = new Run(text.Substring(lastIndex, match.Index - lastIndex));
span.Inlines.Add(run);
lastIndex = match.Index + match.Length;
var hyperlink = new Hyperlink();
hyperlink.Content = match.Value;
hyperlink.NavigateUri = new Uri(match.Value);
span.Inlines.Add(hyperlink);
}
span.Inlines.Add(new Run(text.Substring(lastIndex)));
return span;
}
Related
I'm having a problem with a ListView for Windows Phone:
I'm using a databinding to display all items in a folder, but when I try to iterate through them, I don't get a control, but the class I use to get the items in the LocalFolder of the app (The subjectpicker class)!
SubjectPicker class:
class SubjectPicker {
public static async Task<List<SubjectPicker>> SubjectData() {
List<string> Names = new List<string>();
List<Brush> Colors = new List<Brush>();
List<string> Items = new List<string>();
List<List<TestOrTask>> TestsAndTasks = new List<List<TestOrTask>>();
StorageFolder LocalFolder = ApplicationData.Current.LocalFolder;
var files = await LocalFolder.GetFilesAsync();
foreach (var file in files) {
//await file.DeleteAsync();
List<TestOrTask> TAT = new List<TestOrTask>();
Names.Add(file.DisplayName);
bool readItems = false;
var contents = await FileIO.ReadLinesAsync(file);
foreach (var content in contents) {
if (content.Contains("Subject Color")) {
string colorname = content.Replace("Subject Color: ", "");
Colors.Add(ColorConverter.fromString_Brush(colorname));
}
else if (content == "<Items>") readItems = true;
else if (content == "</Items>") readItems = false;
if (readItems == true && content != "") {
// Layout: <Name>: <Score>/<Maximum>|<YYYY>/<MM>/<DD>
string name = content.Split(':')[0];
string Score = content.Split(':')[1].Replace(" ", "");
string Date = content.Split('|')[1];
TestOrTask TOT = new TestOrTask(
name,
double.Parse(Score.Split('/')[0]),
double.Parse(Score.Split('/')[1]),
new DateTime(
Int32.Parse(Date.Split('/')[0]),
Int32.Parse(Date.Split('/')[1]),
Int32.Parse(Date.Split('/')[2])));
TAT.Add(TOT);
}
}
Items.Add("Aantal Testen & Taken: " + TAT.Count.ToString());
TestsAndTasks.Add(TAT);
}
var data = new List<SubjectPicker>();
for (int i = 0; i < Names.Count; i++) {
data.Add(new SubjectPicker(Names[i], Colors[i], Items[i], TestsAndTasks[i]));
}
return data;
}
public SubjectPicker(string name, Brush color, string itemstotal, List<TestOrTask> TestsAndTasks) {
PickerName = name;
PickerColor = color;
ItemTotal = itemstotal;
this.TestsAndTasks = TestsAndTasks;
}
public string PickerName { get; set; }
public Brush PickerColor { get; set; }
public string ItemTotal { get; set; }
public List<TestOrTask> TestsAndTasks = new List<TestOrTask>();
}
Xaml Code:
<Page.Resources>
<DataTemplate x:Key="SubjectTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Border Margin="0,9.5,0,0" Background="Transparent" >
<Rectangle Height="50" Width="50" Fill="{Binding PickerColor}" RadiusX="5" RadiusY="5"/>
</Border>
<StackPanel Grid.Column="1" Margin="14.5,0,0,0">
<TextBlock Text="{Binding PickerName}" Style="{ThemeResource ListViewItemTextBlockStyle}" />
<TextBlock Text="{Binding ItemTotal}" Style="{ThemeResource ListViewItemSubheaderTextBlockStyle}" />
</StackPanel>
</Grid>
</DataTemplate>
</Page.Resources>
<Page.BottomAppBar>
<CommandBar x:Name="AppBar" Visibility="Collapsed">
<AppBarButton x:Name="EditSubject" Icon="Edit" Label="Aanpassen" Click="EditSubject_Click"/>
<AppBarButton x:Name="DeleteSubject" Icon="Delete" Label="Verwijderen" Click="DeleteSubject_Click"/>
</CommandBar>
</Page.BottomAppBar>
<Grid x:Name="MainGrid" Loaded="MainGrid_Loaded">
<Controls:TopBarControl x:Name="TopBarControl" Margin="0" VerticalAlignment="Top" PageName="Vakken" ControlsVisible="All" Width="Auto" BackButtonClicked="TopBar_BackButtonClicked" AddButtonClicked="TopBar_AddButtonClicked" EditButtonClicked="TopBar_EditButtonClicked"/>
<Grid x:Name="ControlsGrid" Margin="0,50,0,-60" Tapped="ControlsGrid_Tapped">
<ListView x:Name="SubjectList" ItemsSource="{Binding}" ItemTemplate="{StaticResource SubjectTemplate}"/>
</Grid>
</Grid>
Void to iterate through:
private async Task SelectSubjects() {
for (int i = 0; i < SubjectList.Items.Count; i++) {
var control = SubjectList.Items[i];
Grid subject = control as Grid;
if (subject != null) {
subject.Background = new SolidColorBrush(SchoolToolsColors.AppColor);
await Task.Delay(TimeSpan.FromMilliseconds(50));
}
}
isSelecting = true;
AppBarVisible = Visibility.Visible;
}
Thanks in advance!!!
When iterating on databinded control you'll always get its underlying data.
You should Style.Triggers to alter UI based on data (changing background, showing/hiding controls, etc.).
However, there is a way to go with altering UI from C# but that would make XAML and code-behind tightly coupled => that introduces more complexity into your code - simply believe me, it is not what you want/need.
I would like to have string containing Inline markups like
var str = "foo bar <Bold>dong</Bold>"
and feed TextBlock with it so the text would get formatted like it would be added to Inlines collection. How could I achive that?
You could wrap the text with a <TextBlock> tag and parse the whole thing as XAML:
public TextBlock CreateTextBlock(string inlines)
{
var xaml = "<TextBlock xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">"
+ inlines + "</TextBlock>";
return XamlReader.Parse(xaml) as TextBlock;
}
Then use the newly created TextBlock as you like. Put it in some Panel
var str = "foo bar <Bold>dong</Bold>";
grid.Children.Add(CreateTextBlock(str));
or perhaps copy its Inlines to another TextBlock.
You can try the below code.
<TextBlock x:Name="txtBlock"/>
string regexStr = #"<S>(?<Str>.*?)</S>|<B>(?<Bold>.*?)</B>";
var str = "<S>foo bar </S><B>dong</B>";
Regex regx = new Regex(regexStr);
Match match = regx.Match(str);
Run inline = null;
while (match.Success)
{
if (!string.IsNullOrEmpty(match.Groups["Str"].Value))
{
inline = new Run(match.Groups["Str"].Value);
txtBlock.Inlines.Add(inline);
}
else if (!string.IsNullOrEmpty(match.Groups["Bold"].Value))
{
inline = new Run(match.Groups["Bold"].Value);
inline.FontWeight = FontWeights.Bold;
txtBlock.Inlines.Add(inline);
}
match = match.NextMatch();
}
listpicker shows object name instead of property.I'm already try all the solution which already given in stackoverflow but i dn't get any result
BorderThickness="0.2" SelectionMode="Multiple" SelectionChanged="AreaClassification_SelectionChanged"
VerticalAlignment="Bottom" Margin="204,-53,0,0" BorderBrush="Black" >
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<StackPanel >
<TextBlock Text="{Binding name}" Foreground="Black" FontSize="18"/>
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
<toolkit:ListPicker.FullModeItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding name}" FontSize="18" Foreground="White" />
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.FullModeItemTemplate>
</toolkit:ListPicker>
I just need to learn how to design a control.any reference do you have?
CodeBehind
string result = e.Result;
JObject o = JObject.Parse(result);
var results = o["Data"]["Area"];
JArray obj = results as JArray;
var dt = obj.ToArray();
foreach (var d in dt)
{
Area dts = new Area();
dts.code = Convert.ToString(d["code"]);
dts.name = Convert.ToString(d["name"]);
_lst.Add(dts);
}
AreaClassification.ItemsSource = _lst;
After lot of research i found this
AreaClassification.SummaryForSelectedItemsDelegate += SummarizeTeams;
protected string SummarizeTeams(IList selection)
{
string str = "*None Selected*";
if (null != selection && selection.Count > 0)
{
StringBuilder contents = new StringBuilder();
int idx = 0;
foreach (object o in selection)
{
if (idx > 0) contents.Append(", ");
contents.Append(((Area)o).name);
idx++;
}
str = contents.ToString();
}
return str;
}
To Show List Of Item We Need to use SummaryForSelectedItemsDelegate method on c#.I hope It's will help others
Here's my ComboBox:
<ComboBox HorizontalAlignment="Left"
Margin="125,110,0,0"
VerticalAlignment="Top"
Width="120"
DisplayMemberPath="lot_number"
ItemsSource="{Binding LotNumList}"
RenderTransformOrigin="0.583,2" Height="18" />
Here's a DataGrid that I want to update the values to:
<DataGrid HorizontalAlignment="Left" Margin="228,177,0,0" VerticalAlignment="Top"
Height="292" Width="617" ItemsSource="{Binding ComponentsList}"
AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Component" Binding="{Binding component}" CanUserResize="False"/>
<DataGridTextColumn Header="Control" Binding="{Binding aControl}" CanUserResize="False"/>
<DataGridTextColumn Header="Reference" Binding="{Binding cal_ref}" CanUserResize="False" />
<DataGridTextColumn Header="Family" Binding="{Binding family}" CanUserResize="False"/>
<DataGridTextColumn Header="Id" Binding="{Binding componentId }" CanUserResize="False"/>
</DataGrid.Columns>
Here's how I'm grabbing data from db to populate the ComboBox:
//Grabs the lot_number column from db that is distinct
var lotNum = db.LotInformation.GroupBy(i => i.lot_number)
.Select(group => group.FirstOrDefault());
//Loops through the lot numbers column in db and converts to list
foreach (var item in lotNum)
{
Console.WriteLine(item.lot_number);
}
LotNumList = lotNum.ToList();
Now I am wondering how do I connect my ComboBox so that when I select a value in the ComboBox... then the DataGrid gets updated based on the value of the selection in the ComboBox.
I tried something like this:
private void UpdateExistLotList(string LotNumber)
{
using (var db = new DDataContext())
{
//Grabs the lot_number column from db that is distinct
var ExistLot = db.LotInformation.First(l => l.lot_number.Equals(LotNumber));
}
}
Calling the method in my lot number list property but it doesn't get called or just doesn't work. I'm not sure what I'm doing wrong. Any ideas?
EDIT:
Properties:
public List<Components> ComponentsList
{
get
{
return components;
}
set
{
components = value;
RaisePropertyChanged("ComponentsList");
}
}
public string LotNumber
{
get
{
return lotNumber;
}
set
{
lotNumber = value;
RaisePropertyChanged("LotNumber");
}
}
public List<LotInformation> LotNumList
{
get
{
return lotNumList;
}
set
{
lotNumList = value;
RaisePropertyChanged("LotNumList");
UpdateExistLotList(LotNumber);
}
}
Here's where LotNumber is declared (I take the deserialized value of lot number from memory and assign it to LotNumber):
public void DeSerializationXML(string filePath)
{
XmlRootAttribute xRoot = new XmlRootAttribute();
xRoot.ElementName = "lot_information";
xRoot.IsNullable = false;
// Create an instance of lotinformation class.
var lot = new LotInformation();
// Create an instance of stream writer.
TextReader txtReader = new StreamReader(filePath);
// Create and instance of XmlSerializer class.
XmlSerializer xmlSerializer = new XmlSerializer(typeof(LotInformation), xRoot);
// DeSerialize from the StreamReader
lot = (LotInformation)xmlSerializer.Deserialize(txtReader);
// Close the stream reader
txtReader.Close();
//Storing deserialized strings to db
using (var db = new DMIDataContext())
{
LotInformation newLot = new LotInformation();
if (newLot != null)
{
newLot.Id = lot.Id;
newLot.lot_number = lot.lot_number;
newLot.exp_date = lot.exp_date;
LotNumber = newLot.lot_number;
ExpirationDate = newLot.exp_date.ToString();
//Grabs the lot_number column from db that is distinct
var lotNum = db.LotInformation.GroupBy(i => i.lot_number).Select(group => group.FirstOrDefault());
//Loops through the lot numbers column in db and converts to list
foreach (var item in lotNum)
{
Console.WriteLine(item.lot_number);
}
LotNumList = lotNum.ToList();
foreach (Components comp in lot.Components)
{
newLot.Components.Add(comp);
}
ComponentsList = newLot.Components;
foreach (Families fam in lot.Families)
{
newLot.Families.Add(fam);
}
FamiliesList = newLot.Families;
try
{
db.LotInformation.Add(newLot);
db.SaveChanges();
Console.WriteLine("successfully");
}
catch
{
//TODO: Add a Dialog Here
}
}
}
private void UpdateExistLotList()
{
using (var db = new DDataContext())
{
//Grabs the lot_number column from db that is distinct
var ExistLot = db.LotInformation.First(l => l.lot_number.Equals(LotNumber));
}
}
This method has no para?
But you call it like this?
{
lotNumList = value;
RaisePropertyChanged("LotNumList");
UpdateExistLotList(LotNumber);
}
Is there something wrong?
Your question is that UpdateExistLotList was never called?
Try to add a breakpoint on RaisePropertyChanged("LotNumList"); in VisualStudio and observe why it doesn't get called.
In your code, I don't know the use of ExistLot and LotNumber.
I guess your demand is like this?
Comobox shows LotInformation, select one LotInformation and make datagrid shows LotInformation.Components ?
If so,you can Binding DataGrid's Itemsource={Binding Components,ElementName=ComboboxName}
or you can Binding Combobox's SelectedItem/SelectedValue, and then set ComponentsList in these events.
Get your demand.
You means you don't set a relationship between Components and LotInformation in EntityFramework or other DB framework.
If you use EF, I advise you make a relationship between Components and LotInformation,and then you can get ComponentsList by LotInformation.Components.
In another way, try like this:
<ComboBox HorizontalAlignment="Left"
Margin="125,110,0,0"
VerticalAlignment="Top"
Width="120"
DisplayMemberPath="lot_number"
SelectedItem="{Binding SelectedLot}"
ItemsSource="{Binding LotNumList}"
RenderTransformOrigin="0.583,2" Height="18" />
private LotInformation selectedLot;
public LotInformation SelectedLot
{
get { return selectedLot; }
set
{
selectedLot = value;
var lot = value as LotInformation;
if (lot != null)
{
ComponentsList = new List<Components>();
//add ComponentsList
}
}
}
In my WP8 app, i am using the ListPicker control with multi selection option. In my page, i am loading the Listpicker data from a List of custom classes. And i can able to save what objects are user selected based by implementing the SummaryForSelectedItemsDelegate event and i am saving this info to Isolated storage for later access.
My main problem, how to select the above user selected option when user opened this page where this list picker is there?
I tried using the SelectionChanged event and try to select the object based on stored data, it didn't worked.
<toolkit:ListPicker x:Name="userCountryList" ItemsSource="{Binding CountryList}" Header="Choose a country or region:" SelectionMode="Multiple" FullModeItemTemplate="{StaticResource DataTemplate2}" />
<phone:PhoneApplicationPage.Resources>
<DataTemplate x:Key="DataTemplate2">
<StackPanel Orientation="Horizontal">
<TextBlock HorizontalAlignment="Left" FontSize="28" TextWrapping="Wrap" Text="{Binding CountryName}" VerticalAlignment="Center" Width="Auto"/>
</StackPanel>
</DataTemplate>
Code behind:
var brushes = new List<CustomClass>();
brushes.Add(new CustomClass { CountryCode = "US", CountryName = "United States" });
brushes.Add(new CustomClass { CountryCode = "FR", CountryName = "France" });
brushes.Add(new CustomClass { CountryCode = "DE", CountryName = "Germany" });
brushes.Add(new CustomClass { CountryCode = "CA", CountryName = "Canada" });
userCountryList.SummaryForSelectedItemsDelegate = SummarizeItems;
userCountryList.ItemsSource = brushes;
private string SummarizeItems(IList items)
{
if (items != null && items.Count > 0)
{
string summarizedString = "";
if (items != null)
for (int i = 0; i < items.Count; i++)
{
summarizedString += ((CustomClass )items[i]).CountryCode;
// If not last item, add a comma to seperate them
if (i != items.Count - 1)
summarizedString += ", ";
}
if (!AppSettings.Default.UserSelectedMarkets.Equals(summarizedString))
AppSettings.Default.UserSelectedMarkets = summarizedString;
return summarizedString;
}
else
return "Nothing selected";
}
In your page, override OnNavigatedTo and add all objects to userCountryList.SelectedItems:
protected override void OnNavigatedTo(NavigationEventArgs e)
{
foreach (var o in GetSelectedObjects())
{
userCountryList.SelectedItems.Add(o);
}
base.OnNavigatedTo(e);
}