I am writing an application to Windows Store, it will have exercises with questions which can be answered. I created a class Question where I have 2 variables question and answer.
Example of question:
Question q = new Question(
"This is simple [1] of question. This is the [2]",
new string[]
{
"sample",
"end"
});
What I want is to add questions to a grid view as a TextBlock with a question and a TextBox (the place where we will write the answer) in a place of [1] and [2]. So it would look like this:
<TextBox>
<TextBlock> This is simple </TextBlock>
<TextBox/>
<TextBlock> of question. This is the </TextBlock><TextBox/>
I'm not sure if I'm right to do it to look like that. Can I create a method in the Question class which adds items to MainPage.xaml in the way I presented?
I think you should make your own UserControl to display questions. The point is to create controls (TextBlock/TextBox) dynamically in code behind and add them as children to the WrapPanel. I can't see simple only-XAML decision here.
XAML:
<UserControl <!--skipped--> >
<WrapPanel x:Name="mainPanel">
</WrapPanel>
</UserControl>
Code behind:
partial class QuestionControl : UserControl
{
public QuestionControl()
{
InitializeComponent();
}
private Question _question = null;
public Question Question
{
get { return _question; }
set
{
_question = value;
UpdateUI();
}
}
private void UpdateUI()
{
mainPanel.Children.Clear();
if (this.Question != null)
{
List<FrameworkElement> controls = new List<FrameworkElement>();
string[] questionSegments = // some code to split question here
foreach (var qs in questionSegments)
{
controls.Add(new TextBlock() { Text = qs } );
}
for (int i = 0; i < this.Question.AnswerStrings.Length; i++)
{
string answer = this.Question.AnswerStrings[i];
TextBox newTextBox = new TextBox();
controls.Insert(i * 2 + 1, newTextBox); // inserting TextBoxes between TextBlocks
}
foreach (var control in controls)
{
mainPanel.Children.Add(control); // adding all the controls to the wrap panel
}
}
}
}
Related
I have a collection of items with a string property. That string property contains text which includes 6 digit numbers in various places like so:
this string 123456 is an example of a set of links 884555 to the following numbers
401177
155879
998552
I want to turn those 6 digit numbers into hyperlinks that when clicked will run a command on the ViewModel passing themselves as parameters. For example if I click 401177 I want to run HyperlinkCommand on the VM with the string parameter "401177". I still want to keep the formatting of the original text.
I figured the best way to do it would be with a custom control based on TextBlock. Below is the rough structure of my view, the UserControl is bound to the ViewModel, I use a ContentControl to bind to a collection of items with the property "detail", and that is templated with the custom text block bound to the "detail" property of my items.
<UserControl.DataContext>
<VM:HdViewModel/>
</UserControl.DataContext>
<UserControl.Resources>
<DataTemplate x:Key="DetailTemplate">
<StackPanel Margin="30,15">
<helpers:CustomTextBlock FormattedText="{Binding detail}"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<Grid>
<ContentControl Content="{Binding ItemListing}" ContentTemplate="{StaticResource DetailTemplate}" />
</Grid>
I used the code from this question and edited it slightly to generate the following custom control:
public class CustomTextBlock : TextBlock
{
static Regex _regex = new Regex(#"[0-9]{6}", RegexOptions.Compiled);
public static readonly DependencyProperty FormattedTextProperty = DependencyProperty.RegisterAttached("FormattedText", typeof(string), typeof(CustomTextBlock), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsMeasure, FormattedTextPropertyChanged));
public static void SetFormattedText(DependencyObject textBlock, string value)
{
textBlock.SetValue(FormattedTextProperty, value);
}
public static string GetFormattedText(DependencyObject textBlock)
{ return (string)textBlock.GetValue(FormattedTextProperty); }
static void FormattedTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(d is TextBlock textBlock)) return;
var formattedText = (string)e.NewValue ?? string.Empty;
string fullText =
$"<Span xml:space=\"preserve\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">{formattedText}</Span>";
textBlock.Inlines.Clear();
using (var xmlReader1 = XmlReader.Create(new StringReader(fullText)))
{
try
{
var result = (Span)XamlReader.Load(xmlReader1);
RecognizeHyperlinks(result);
textBlock.Inlines.Add(result);
}
catch
{
formattedText = System.Security.SecurityElement.Escape(formattedText);
fullText =
$"<Span xml:space=\"preserve\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">{formattedText}</Span>";
using (var xmlReader2 = XmlReader.Create(new StringReader(fullText)))
{
try
{
dynamic result = (Span)XamlReader.Load(xmlReader2);
textBlock.Inlines.Add(result);
}
catch
{
//ignored
}
}
}
}
}
static void RecognizeHyperlinks(Inline originalInline)
{
if (!(originalInline is Span span)) return;
var replacements = new Dictionary<Inline, List<Inline>>();
var startInlines = new List<Inline>(span.Inlines);
foreach (Inline i in startInlines)
{
switch (i)
{
case Hyperlink _:
continue;
case Run run:
{
if (!_regex.IsMatch(run.Text)) continue;
var newLines = GetHyperlinks(run);
replacements.Add(run, newLines);
break;
}
default:
RecognizeHyperlinks(i);
break;
}
}
if (!replacements.Any()) return;
var currentInlines = new List<Inline>(span.Inlines);
span.Inlines.Clear();
foreach (Inline i in currentInlines)
{
if (replacements.ContainsKey(i)) span.Inlines.AddRange(replacements[i]);
else span.Inlines.Add(i);
}
}
static List<Inline> GetHyperlinks(Run run)
{
var result = new List<Inline>();
var currentText = run.Text;
do
{
if (!_regex.IsMatch(currentText))
{
if (!string.IsNullOrEmpty(currentText)) result.Add(new Run(currentText));
break;
}
var match = _regex.Match(currentText);
if (match.Index > 0)
{
result.Add(new Run(currentText.Substring(0, match.Index)));
}
var hyperLink = new Hyperlink();
hyperLink.Command = ;
hyperLink.CommandParameter = match.Value;
hyperLink.Inlines.Add(match.Value);
result.Add(hyperLink);
currentText = currentText.Substring(match.Index + match.Length);
} while (true);
return result;
}
}
This is showing the links properly, however I dont know how to bind to the command on my ViewModel. I tested the command and the parameter using a button previously, and the binding was
Command="{Binding DataContext.HyperlinkCommand, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"
CommandParameter="{Binding Content, RelativeSource={RelativeSource Self}}"
So what I am hoping is that I can convert this XAML into C# and attach it to hyperLink.Command = in my custom control. I can't figure out how to access the DataContext of the UserControl that the CustomTextBlock will be placed in.
I am not under any illusion that what I am doing is the best or right way of doing things so I welcome any suggestions
This is an interesting challenge, which I have solved with new code - coming at the problem in a slightly different way:
The code can be found here:
https://github.com/deanchalk/InlineNumberLinkControl
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
Display data from API, into WPF application.
Hello, Im creating an application for a questionnaire. It calls the data from the API and i would like to know what is the best way to display the information.
The information displayed will have to be "questionnaire type" and will be attempted by users and saved into database with the selected values.
Thanks
I already tried through textboxes that are dynamically created from a List, but setting the location of the textboxes tend to be full of errors
Please have a look at StackPanel
It stacks its child elements below or beside each other, dependening on its orientation.
With a stack panel you can place multiple elements atop of each other, see the sample from the link provided
<StackPanel>
<TextBlock Margin="10" FontSize="20">How do you like your coffee?</TextBlock>
<Button Margin="10">Black</Button>
<Button Margin="10">With milk</Button>
<Button Margin="10">Latte machiato</Button>
<Button Margin="10">Chappuchino</Button>
</StackPanel>
this will result in the following layout
You can create these dynamically. Take the following template
<StackLayout x:Name="QuestionStack">
<TextBlock Margin="10" FontSize="20" x:Name="QuestionTextBlock" />
</StackLayout>
and in your code behind
void DisplayQuestion(Question question)
{
QuestionTextBlock.Text = question.QuestionText;
foreach(var answer in question.Answers)
{
AddAnswerButton(answer);
}
}
private void AddAnswer(Answer answer)
{
QuestionStack.Children.Add(CreateButtonForAnswer(answer));
}
private Button CreateButtonForAnswer(Answer answer)
{
var button new Button()
{
Content = answer.Text,
Margin = 10
}
button.Click += (sender, eventArgs) =>
{
// handle button click
};
return button;
}
Please note: This is only one possibility. Without knowing more about your requirements it's hard to tell what you need exactly.
Edit:
Since you asked: You could for example create a custom control to display one question (I've replaced the Button with CheckBox and for sake of simplicity I've omitted the XAML, but it is not too hard to achieve the same results with XAML)
class QuestionControl : ContentControl
{
private Question question;
private StackLayout QuestionStackLayout { get; }
public QuestionControl()
{
QuestionStackLayout = new StackLayout();
QuestionTextBlock = new TextBlock()
{
Margin = 10,
FontSize = 20
};
QuestionStackLayout.Children.Add(QuestionTextBlock);
}
public Question Question
{
get
{
return question;
}
set
{
question = value;
DisplayQuestion();
}
}
private void DisplayQuestion()
{
QuestionTextBlock.Text = question.QuestionText;
foreach(var answer in question.Answers)
{
AddAnswerButton(answer);
}
}
private void AddAnswer(Answer answer)
{
QuestionStack.Children.Add(CreateButtonForAnswer(answer));
}
private CheckBox CreateCheckBoxForAnswer(Answer answer)
{
var checkBox new CheckBox()
{
Content = answer.Text,
Margin = 10
}
checkBox.Checked += (sender, eventArgs) =>
{
answer.IsSelected = (sender as CheckBox).IsChecked;
};
return checkBox;
}
}
You can now stack instances of QuestionControl. Since the Answer-objects are updated by the CheckBox.Click event you can simply access QuestionControl.Question to get which answers are selected. You might think about deep copying the Question when setting QuestionControl.Question instead of just setting the reference, to avoid side effects.
I am pretty new to C#, I've been having this problem for a little while now. I have a ListBox named "listBox" which is bound to an Items Collection which has over 100 checkbox, each with a different content (or checkbox label).
I am trying to loop through each checkbox, and if it is checked, print the content into the console. I am able to detect if something is checked or not, however is definitely NOT the most efficient way of doing it. See my code below:
private void SomeButton_Click(object sender, RoutedEventArgs e)
{
string checkState = "";
for (int i = 0; i < listBox.Items.Count - 1; i++)
{
checkState = listBox.Items[i].ToString().Substring(listBox.Items[i].ToString().Length - 5, 5);
if (checkState != "False")
{
Console.WriteLine("Item " + i + " is checked");
}
}
}
This code does work for detecting if something is checked or not properly. However it'd be more efficient if I was able to get the actual true/false property from the checkboxes in the ItemsCollection. I have tried numerous ways to attempt to get the checked state, as well as the checkbox content, but sadly I am coming up dry in every attempt. Here are a few of the things I have tried to get the content from one of these checkboxes:
Console.WriteLine(listBox.Items[i].ToString());
Console.WriteLine(listBox.Items.GetItemAt(i).ToString());
Console.WriteLine(listBox.Items.GetItemAt(i).Equals(0).ToString());
Any help would be greatly appreciated.
What you ideally want is to have a class of data objects in your collection, not a collection of UI objects.
public class MyDataObject : INotifyPropertyChanged
{
public string Caption { get; set; }
public bool IsChecked { get; set; }
}
and
var items = new ObservableCollection<MyDataObject>();
// TODO: populate collection
listBox.ItemsSource = items;
Now when you bind it, listbox.Items contains your ObservableCollection<MyDataObject>, and you can just check the values from there
private void SomeButton_Click(object sender, RoutedEventArgs e)
{
foreach(MyDataObject item in listBox.Items)
{
if (item.IsChecked)
Console.WriteLine("Item " + item.Caption + " is checked");
}
}
As a side note, if you don't need selection behavior an ItemsControl might be a better fit for a series of controls than a ListBox. The XAML would probably look something like this :
<ItemsControl x:Name="myItemsControl">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Caption}" Checked="{Binding IsChecked}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
var items = new ObservableCollection<MyDataObject>();
items.Add(new MyDataObject() { Caption = "A" });
items.Add(new MyDataObject() { Caption = "B" });
items.Add(new MyDataObject() { Caption = "C" });
items.Add(new MyDataObject() { Caption = "D" });
items.Add(new MyDataObject() { Caption = "E" });
myItemsControl.ItemsSource = items;
Situation : i have a textbox control located within DataTemplate of <phone:Panorama.TitleTemplate> tags
<phone:Panorama.TitleTemplate>
<DataTemplate>
<TextBlock Text="select your problem" Margin="7,40,0,0"
FontSize="60" Foreground="{StaticResource PhoneForegroundBrush}"/>
</DataTemplate>
</phone:Panorama.TitleTemplate>
now i have another button located outside of DataTemplate tag and within LayoutRoot Grid tag . this button has a click event whose definition is present in the code behind cs file .
Problem :
i want to access the textbox within the event handler of this button . How do i do it ?
You could use the VisualTreeHelper.
Try this snippet which is for a listbox, you could modify it:
public string option_selected = "";
public int check_count = 0;
public void SearchElement(DependencyObject targeted_control)
{
var count = VisualTreeHelper.GetChildrenCount(targeted_control); // targeted_control is the listbox
if (count > 0)
{
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(targeted_control, i);
if (child is TextBlock) // specific/child control
{
TextBlock targeted_element = (TextBlock)child;
if (targeted_element.IsChecked == true)
{
if (targeted_element.Tag!= null)
{
option_selected = targeted_element.Tag.ToString();
}
return;
}
}
else
{
SearchElement(child);
}
}
}
else
{
return;
}
}
This would be a great sample you could go through How to access a specific item in a Listbox with DataTemplate?
Hope it helps!
I am working on a little project for a contest in my city..and i just hit a brick wall.The thing is: i am creating a userControl in Blend(let's say a canvas,in wich i have a reactangle..a textblock and an image).My problem is that i can not add this to the listboxitem in WPF by code.Addin the userControl one by one in the designer seems to work..but the software is going to work with a variable number of items for the listbox.
private void mainPane1_Loaded(object sender, RoutedEventArgs e)
{
MessageBox.Show("layout updated");
questions cq;
Button btn;
for (int i = 1; i <= 10; i++)
{
btn = new Button();
btn.Content = "intreb" + i.ToString();
cq = new questions();
Canvas.SetZIndex(cq, 17);
//cq.questionHolder.Text = "intrebare" + i.ToString();
listaintrebari.Items.Add(cq);
MessageBox.Show("intrebare" + i.ToString());
listaintrebari.Items.Add(btn);
//MessageBox.Show("layout updated");
}
}
questions is my UserControl and listaintrebari is the listbox.I tried to add some buttons and it works great...but it seems to hate my userControl.
I am waiting for your thoughts on how to resolve this issue, and if you have any sugestions on what other is best to use in my situation and how..it would be great.Thank you!
Ok, here's some actual code that might help you out.
I will be using several WPF concepts that you might want to study further : DataBinding, DataTemplates, ImageSources, ObservableCollections
First you need to create (if you don't have it yet) an underlying class for your Questions. The simplest you can get would be something like this :
internal class Question
{
public ImageSource QuestionImage { get; set; }
public string QuestionText { get; set; }
}
Then in your screen's code behind (yes, we are not at MVVM yet), you should create an ObservableCollection of Question and pouplate them with your questions
I have smth like this:
public ObservableCollection<Question> Questions { get; private set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
Questions = new ObservableCollection<Question>();
for (int i = 1; i <= 10; i++)
{
var newQ = new Question { QuestionText = "intrebarea " + i.ToString(), QuestionImage = _get your image as a ImageSource here_ };
Questions.Add(newQ);
}
}
The this.DataContext = this is very important, otherwise your Data Binding will not work.
In your design area, create a list and bind it to the Questions collection you created. The way the question is displayed in the list is driven by the "ItemTemlpate" as below.
<ListBox ItemsSource="{Binding Questions}">
<ListBox.ItemTemplate>
<StackPanel>
<Image Source="{Binding QuestionImage}" Height="20" Width="20"/>
<TextBlock Margin="5" Text="{Binding QuestionText}" />
</StackPanel>
</ListBox.ItemTemplate>
</ListBox>
You can replace the I have there with your UserControl contents or event the UserControl itself, but make sure to preserve the Bindings to the objects in your Question class.
Like I said above, many things might not make sense at this point so make sure you read about them : What is a data Binding, What is a DataContext, What is an ObservableCollection. Also, try looking at MVVM when you get a chance...
Lastly, if you are unsure how to get an ImageSource when you have a jpg or png file in your project:
public ImageSource GetImagesource(string location)
{
var res = new BitmapImage()
res.BeginInit();
res.UriSource = new Uri("pack://application:,,,/ApplicationName;component/" + location);
res.EndInit();
return res;
}
The right way to handle this kind of situation is by having a data model with a collection of your questions. Then bind your ListBox.ItemsSource to the collection and provide a DataTemplate that uses your UserControl.
Use the ListBox's ItemTemplate to define what you want each instance of your object to look like, then bind the ListBox's ItemsSource to a collection of that type.
You need to create a collection (e.g. List) of your control and bind the collection to the ListBox.