I need to create a (WP7) chart containing multiple series. The data I'm trying to visualize:
F.ex following collection, containing UserName, Date, Points:
User1,2011-11-09,6
User2,2011-11-09,8
User1,2011-11-02,9
User2,2011-11-02,8
There can be more than two users in the data.
XAML Namespace
xmlns:chartingToolkit="clr-namespace:System.Windows.Controls.DataVisualization.Charting;assembly=System.Windows.Controls.DataVisualization.Toolkit"
CodeBehind
mdChart.Series.Add(MDSeries);
mdChart.Title = "Statistics";
MDSeries.SetBinding(ColumnSeries.ItemsSourceProperty, new Binding());
MDSeries.ItemsSource = lCompetitionStats;
MDSeries.DependentValuePath = "Points";
series.IndependentValuePath = "Date";
MDSeries.IndependentValuePath = "UserName";
XAML
<controls:PanoramaItem Header="mdscores">
<Grid>
<charting:Chart x:Name="mdChart" Foreground="Blue" Background="Black">
<charting:ColumnSeries Background="Black"/>
</charting:Chart>
</Grid>
</controls:PanoramaItem>
As a result, i have the users on X-axis and Points on the Y-axis. I would need dates on X-axis, Points on Y-Axis and a separate serie for earch user. How to create/populate such a chart runtime?
Try the following:
CodeBehind
public class ColumnChartValues
{
#region Variables
public string Label { get; private set; }
public double Value { get; private set; }
#endregion
#region Constructor and Initialization
public ColumnChartValues()
{
Label = string.Empty;
Value = 0;
}
public ColumnChartValues(string label, double value)
{
Label = label;
Value = value;
}
#endregion
}
public partial class StatsPage : Page
{
public ObservableCollection<ColumnChartValues> ColumnChartDatabase { get; private set; }
public StatsPage()
{
DataContext = this;
InitializeColumnChart();
}
private void InitializeColumnChart()
{
mdChart.Title = "Statistics";
ColumnChartDatabase = new ObservableCollection<BarChartValues>();
ColumnChartDatabase.Add(new ColumnChartValues("Value One: ", 1));
ColumnChartDatabase.Add(new ColumnChartValues("Value Two: ", 2));
ColumnChartDatabase.Add(new ColumnChartValues("Value Three: ", 3));
}
}
XAML
<charting:Chart x:Name="mdChart" Foreground="Blue" Background="Black">
<charting:ColumnSeries Background="Black" ItemsSource="{Binding ColumnChartDatabase}" IndependentValueBinding="{Binding Label}" DependentValueBinding="{Binding Value}"/>
</charting:Chart>
FYI
You will have to edit the ColumnChartValues chart to suit your needs
Related
I am creating a chat application in xamarin.forms.What I am trying to achieve is whenever user typed message contains a URL, that should be highlighted and provide click to it.For this feature I found Span in Label text.When user click on send button of chat , I will check for URL and make it as another span.I got this idea from Lucas Zhang - MSFT form this question here.
The problem is I am trying to do the spanning in view model and the individual chat bubble is in another view cell which will call as ItemTemplate in my chat listview. Anyway the spanning is not working as I intended ie; it doesn't highlight .
My view Model.
public Queue<Message> DelayedMessages { get; set; } = new Queue<Message>();
public ObservableCollection<Message> Messages { get; set; } = new ObservableCollection<Message>();
public string TextToSend { get; set; }
public ChatPageViewModel()
{
OnSendCommand = new Command(() =>
{
if (!string.IsNullOrEmpty(TextToSend))
{
var urlStr = TextToSend;
int startIndex = 0, endIndex = 0;
if (urlStr.Contains("www."))
{
startIndex = urlStr.IndexOf("www.");
}
if (urlStr.Contains(".com"))
{
endIndex = urlStr.IndexOf(".com") + 3;
}
if (startIndex != 0 || endIndex != 0)
{
var formattedString = new FormattedString();
Span span1 = new Span() { Text = urlStr.Substring(0, startIndex), TextColor = Color.Black };
formattedString.Spans.Add(span1);
Span span2 = new Span() { Text = urlStr.Substring(startIndex, endIndex - startIndex + 1), TextColor = Color.LightBlue };
span2.GestureRecognizers.Add(new TapGestureRecognizer()
{
NumberOfTapsRequired = 1,
Command = new Command(() => {
})
});
formattedString.Spans.Add(span2);
Span span3 = new Span() { Text = urlStr.Substring(endIndex, urlStr.Length - 1 - endIndex), TextColor = Color.Black };
formattedString.Spans.Add(span3);
var message = new Message
{
Text = formattedString.ToString(),
IsIncoming = false,
MessageDateTime = DateTime.Now
};
Messages.Add(message);
TextToSend = string.Empty;
}
else
{
var message = new Message
{
Text = urlStr.ToString(),
IsIncoming = false,
MessageDateTime = DateTime.Now
};
Messages.Add(message);
TextToSend = string.Empty;
}
}
});
}
Single chat Bubble XAML
<Label x:Name="OutgoingMessage" TextColor="White" FormattedText="{Binding Text}" HorizontalOptions="End" >
</Label>
My Chat page XAML
<Grid RowSpacing="0" Margin="0,20,0,0"
ColumnSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="1" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListView Grid.Row="0"
ItemTemplate="{StaticResource MessageTemplateSelector}"
ItemsSource="{Binding Messages,Mode=OneWay}"
Margin="0"
SelectionMode="None"
FlowDirection="RightToLeft"
HasUnevenRows="True" x:Name="ChatList"
VerticalOptions="FillAndExpand"
SeparatorColor="Transparent"
>
</ListView>
<BoxView HorizontalOptions="FillAndExpand"
HeightRequest="1"
BackgroundColor="#F2F3F5"
Grid.Row="1"/>
<partials:ChatInputBarView Grid.Row="2"
Margin="0,0,0,0"
x:Name="chatInput"/>
</Grid>
ChatPage.xaml.cs
public partial class ChatPage : ContentPage
{
ChatPageViewModel vm;
public ChatPage()
{
InitializeComponent();
this.BindingContext = vm= new ChatPageViewModel();
}
}
Messages class
public class Message : ObservableObject
{
string text;
public string Text
{
get { return text; }
set { SetProperty(ref text, value); }
}
DateTime messageDateTime;
public DateTime MessageDateTime
{
get { return messageDateTime; }
set { SetProperty(ref messageDateTime, value); }
}
public string MessageTimeDisplay => MessageDateTime.Humanize();
bool isIncoming;
public bool IsIncoming
{
get { return isIncoming; }
set { SetProperty(ref isIncoming, value); }
}
}
Any Help is appreciated.
EDIT:
This question was actually continuation of question. Previously I used AwesomeHyperLinkLabel fromlink. The problem was I cant manage the click event of that label.Thats why I moved with label span.Thanks to Leo Zhu - MSFT For the render changes.
For Android:
[assembly: ExportRenderer(typeof(AwesomeHyperLinkLabel), typeof(AwesomeHyperLinkLabelRenderer))]
namespace App18.Droid
{
public class AwesomeHyperLinkLabelRenderer : LabelRenderer
{
public AwesomeHyperLinkLabelRenderer(Context context) : base(context)
{
}
protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
{
base.OnElementChanged(e);
var view = (AwesomeHyperLinkLabel)Element;
if (view == null) return;
TextView textView = new TextView(Forms.Context);
textView.LayoutParameters = new LayoutParams(LayoutParams.WrapContent, LayoutParams.WrapContent);
textView.SetTextColor(view.TextColor.ToAndroid());
// Setting the auto link mask to capture all types of link-able data
textView.AutoLinkMask = MatchOptions.All;
// Make sure to set text after setting the mask
textView.Text = view.Text;
AddHyperlinksManually(textView);
//textView.SetTextSize(ComplexUnitType.Dip, (float)view.FontSize);
// overriding Xamarin Forms Label and replace with our native control
SetNativeControl(textView);
}
public static void AddHyperlinksManually(TextView _tv)
{
SpannableStringBuilder currentSpan = new SpannableStringBuilder(_tv.Text);
Linkify.AddLinks(currentSpan, MatchOptions.WebUrls);
var objects = currentSpan.GetSpans(0, currentSpan.Length(), Java.Lang.Class.FromType(typeof(URLSpan)));
var urlSpans = new URLSpan[objects.Length];
for (var i = 0; i < urlSpans.Length; i++)
{
urlSpans[i] = objects[i] as URLSpan;
}
foreach (URLSpan _url in urlSpans)
{
int iStart = currentSpan.GetSpanStart(_url);
int iEnd = currentSpan.GetSpanEnd(_url);
currentSpan.RemoveSpan(_url);
currentSpan.SetSpan(new CustomURLSpan(_url.URL), iStart, iEnd, SpanTypes.InclusiveInclusive);
_tv.SetText(currentSpan, TextView.BufferType.Normal);
_tv.MovementMethod = LinkMovementMethod.Instance;
}
}
public class CustomURLSpan : ClickableSpan
{
string mTargetURL;
public CustomURLSpan(string _url) {
mTargetURL =_url;
}
public override void OnClick(Android.Views.View widget)
{
//here you could handle the click event,and you could use MessagingCenter to send mTargetURL to your Page.
Console.WriteLine("Click");
}
}
}
The mistake was with my model.Changed string to FormattedString and also changed in the viewmodel
public class Message : ObservableObject
{
FormattedString text;
public FormattedString Text
{
get { return text; }
set { SetProperty(ref text, value); }
}
DateTime messageDateTime;
public DateTime MessageDateTime
{
get { return messageDateTime; }
set { SetProperty(ref messageDateTime, value); }
}
public string MessageTimeDisplay => MessageDateTime.Humanize();
bool isIncoming;
public bool IsIncoming
{
get { return isIncoming; }
set { SetProperty(ref isIncoming, value); }
}
}
I am trying to bind a response to a custom ListView in Xamarin.Forms but the ListView remains blank even when the ObservableCollection contains results. Please point out my mistake in the code below.
In the below code, I am trying to achieve following result -
The response received from Firebase is serialized and then a custom calendar is created based on the response.
To create the calendar, I am using a Model which will contain all the necessary bindings to be displayed in the View. e.g Color, title, counts etc.
I am using Prism MVVM architecture for building the app.
View
<ListView ItemsSource="{Binding ListItems}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Frame BorderColor="LightGray" Padding="5">
<StackLayout Orientation="Horizontal">
<!--<BoxView BackgroundColor="{Binding HighlightColor}" WidthRequest="10"/>-->
<Label Text="{Binding Date}" FontSize="Medium" Margin="20,0,0,0"/>
<Label Text="{Binding Day}" FontSize="Medium" Margin="20,0,0,0"/>
<Label Text="{Binding LeaveCount}" HorizontalOptions="EndAndExpand" HorizontalTextAlignment="End"/>
</StackLayout>
</Frame>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
ViewModel
public ObservableCollection<CalendarItem> ListItems { get; set; }
public LeaveViewerPageViewModel(INavigationService navigationService, IFirebaseService firebaseService)
: base(navigationService, firebaseService)
{
Title = "View Leaves";
ViewCommand = new DelegateCommand(ViewLeaves);
}
private async void ViewLeaves()
{
ListItems = new ObservableCollection<CalendarItem>();
var x = await FirebaseService.GetAll(_month, _year);
foreach (var item in x)
{
ListItems.Add(item);
}
}
Service
public async Task<List<CalendarItem>> GetAll( string month, string year)
{
List<CalendarItem> ItemList = new List<CalendarItem>();
int iterationLimit = DateTime.DaysInMonth(int.Parse(year), (DateTime.ParseExact(month.Substring(0, 3), "MMM", CultureInfo.InvariantCulture)).Month);
for (int i = 1; i <= iterationLimit; i++)
{
CalendarItem ITEM = new CalendarItem();
DateTime dateTime = new DateTime(int.Parse(year), DateTime.ParseExact(month.Substring(0, 3), "MMM", CultureInfo.InvariantCulture).Month, i);
var res = await GetLeavesAsync(i, month, year);
ITEM.Date = dateTime.Day.ToString();
ITEM.Day = dateTime.DayOfWeek.ToString();
ITEM.LeaveCount = res.Count;
ITEM.Leaves = res;
if (res.Count > 0)
ITEM.HighlightColor = res.Count < 5 ? System.Drawing.Color.Yellow : System.Drawing.Color.Tomato;
ItemList.Add(ITEM);
}
return ItemList;
}
Model
public class CalendarItem
{
public Color HighlightColor { get; set; }
public string Date { get; set; }
public string Day { get; set; }
public int LeaveCount { get; set; }
public List<LeaveDetail> Leaves { get; set; }
}
Based on the code you have here there are actually a couple of issues:
1) Your ListItems property in the ViewModel is not itself implementing INotifyPropertyChanged.
public class ViewAViewModel : BindableBase
{
private ObservableCollection<CalendarItem> _listItems;
public ObservableCollection<CalendarItem> ListItems
{
get => _listItems;
set => SetProperty(ref _listItems, value);
}
}
2) You are newing up a ListItems in the ViewLeaves method and then adding each item one at a time. The result here is that if you followed the first step when you set ListItems it would try to update the UI, and then every time you add an item it will try to update the UI.
3) You have two options for optimizing this:
Use ObservableRangeCollection which James Montemagno has in his MvvmHelpers library. This will allow you to new up the collection in the ctor and then simply dump a collection in like:
private async void ViewLeaves()
{
ListItems.ReplaceRange(await FirebaseService.GetAll(_month, _year));
}
Simply implement the property as shown in the first issue I pointed out but change it to IEnumerable<CalendarItem> and simply set it like:
private async void ViewLeaves()
{
ListItems = await FirebaseService.GetAll(_month, _year);
}
Please try modifying your ListItems as follows:-
private ObservableCollection<CalendarItem> _listItems = new ObservableCollection<CalendarItem>();
public ObservableCollection<CalendarItem> ListItems
{
get => _listItems;
set => SetProperty(ref _listItems, value);
}
I am trying to update the list of progress bars to show the image download progress using Task Parallel Library in Xamarin.Forms
For now I have written a block of code to simulate the download process by using a delay.
Here is my Xaml file where a ListView named MediaList resides with one caption and progress bar per item.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ImageTask.View.ImageTaskView">
<ContentPage.Content>
<ListView ItemsSource="{Binding MediaList}" CachingStrategy = "RecycleElement" VerticalOptions="FillAndExpand" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical">
<Label Text = "{Binding mediaName}" FontSize="22" />
<ProgressBar Progress="{Binding mediaProgress}"></ProgressBar>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
Here is my view model in which I have created one action block that takes the entire list of media object and tries to update the progress bar.
However, my main issue is I am not able to see the progress updated in my UI , so I don't know how shall I update my UI.
public class ImageTaskViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private IList<MediaInfo> _mediaList;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public IList<MediaInfo> MediaList
{
get { return _mediaList; }
set
{
_mediaList = value;
OnPropertyChanged("MediaList");
}
}
public ImageTaskViewModel()
{
Action<IList<MediaInfo>> progressActionBlock = mediaInfoList =>
{
// infinite loop to simulate download
while (true)
{
IEnumerator<MediaInfo> dataList = mediaInfoList.GetEnumerator();
Task.Delay(2000).Wait();
while (dataList.MoveNext())
{
MediaInfo mediaInfo = dataList.Current;
Debug.WriteLine("media name " + mediaInfo.mediaName + " progress " + mediaInfo.mediaProgress);
if (mediaInfo.mediaProgress == 1)
{
Debug.WriteLine("media name " + mediaInfo.mediaName + " Done ");
break;
}
else
{
mediaInfo.mediaProgress = mediaInfo.mediaProgress + 0.1;
}
}
}
};
var opts = new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = 2
};
var progressAction = new ActionBlock<IList<MediaInfo>>(progressActionBlock, opts);
MediaList = new List<MediaInfo>();
for (int i = 1; i < 6; i++)
{
MediaInfo mediaInfo = new MediaInfo();
mediaInfo.mediaName = i.ToString();
MediaList.Add(mediaInfo);
}
// Exectue Action block
progressAction.Post(MediaList);
}
}
Model of MediaInfo:
public class MediaInfo
{
public string mediaId { get; set; }
public string mediaName { get; set; }
public string mediaPath { get; set; }
public byte[] mediaStream { get; set; }
public double mediaProgress { get; set; } = 0.1;
}
In your View model you don't have mediaProgress. You should add
Double _mediaProgress;
public Double mediaProgress{
get{
return _mediaProgress;
} set{
_mediaProgress = value;
OnPropertyChanged("Email");
}
}
And then in your all method you use mediaProgress to set the progress.
I need to bind the names and date of creation of some specific files in isolated storage with datasource of amq:SerialChart. (windows phone 7.8)
I tried making a view model for this.
public class MainViewModel
{
public MainViewModel()
{
var appStorage = IsolatedStorageFile.GetUserStoreForApplication();
var items = new List<ChartItemViewModel>();
string[] fileName = appStorage.GetFileNames("issue*");
foreach( string file in fileName)
{
items.Add(new ChartItemViewModel { Month =file, Value=appStorage.GetCreationTime(file).ToString()});
}
var chart = new ChartViewModel { Items = items };
this.Charts = Enumerable.Repeat(0, 5).Select(_ => chart).ToList();
}
public List<ChartViewModel> Charts { get; set; }
}
}
but I am getting exception when I run this. The xaml code for the same is
<controls:PivotItem Header="Line">
<amq:SerialChart Style="{StaticResource chartStyle}"
DataSource="{Binding Charts[0].Items}"
CategoryValueMemberPath="Month">
<amq:SerialChart.Graphs>
<amq:LineGraph ValueMemberPath="Value" Title="Test Chart" />
</amq:SerialChart.Graphs>
</amq:SerialChart>
</controls:PivotItem>
I want to use best way to show images in my PanoramaPage. I download one page and shows it´s information a then I want to async load another page with images. So I am using HttpWebRequest and I get response. Everything is okay and hope this is best way for these. So I create my GaleryViewModel and for all images at page I add url to my class.
And there is a problem. I can´t see images in view. This i my view:
<ListBox ItemsSource="{Binding Images}" x:Name="listImages" Height="652" Canvas.Top="80">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="0,0,0,17">
<Image Height="100" Width="100" Margin="12,0,9,0" >
<Image.Source>
<BitmapImage UriSource="{Binding ImgURL}" CreateOptions="BackgroundCreation"/>
</Image.Source>
</Image>
<TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
This is content of my WebResponse event handler:
MovieExt movie = this.DataContext as MovieExt;
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(response);
var photos = from ti in doc.DocumentNode.Descendants("div")
where ti.Attributes["class"] != null && ti.Attributes["class"].Value == "photo"
select ti;
Regex rgx = new Regex("http://[0-9a-zA-Z_./]+");
foreach (var photo in photos)
{
GaleryViewModel fotka = new GaleryViewModel();
string style = photo.Attributes["style"].Value;
MatchCollection matches = rgx.Matches(style);
if (matches.Count > 0)
{
foreach (Match match in matches)
fotka.ImgURL = match.Value;
}
fotka.LineOne = "Test";
movie.Images.Add(fotka);
}
this.DataContext = movie;
this.listImages.ItemsSource = movie.Images;
and for all GaleryViewModel and MovieExt:
public class GaleryViewModel : INotifyPropertyChanged
{
private string _imgUrl;
public string ImgURL
{
get
{
return _imgUrl;
}
set
{
if (value != _imgUrl)
{
_imgUrl = value;
NotifyPropertyChanged("ImgURL");
}
}
}
private string _lineOne;
public string LineOne
{
get
{
return _lineOne;
}
set
{
if (value != _lineOne)
{
_lineOne = value;
NotifyPropertyChanged("LineOne");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (null != handler)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class MovieExt
{
public string Title { get; set; }
public string Year { get; set; }
public string Url { get; set; }
...
public List<GaleryViewModel> Images { get; set; }
...
}
I am not sure what I am doing wrong but I think that is something with binding. Thanks for help
Looking at this it looks like you haven't notified that the MovieExt.Images property has changed. Without doing this your ListBox won't update its Items. To do this MovieExt will also need to support INotifyPropertyChanged.
You have to convert Uri to BitmapImage, because BitmapImage UriSource is a stream. Please take a look at this: Image UriSource and Data Binding
Replace loading method (just for test) to:
MovieExt movie = new MovieExt();
movie.Images = new List<GaleryViewModel>();
GaleryViewModel fotka1 = new GaleryViewModel();
fotka1.LineOne = "line1";
fotka1.ImgURL = "http://proservice.kiev.ua/wp-content/uploads/2011/10/apple-logo.jpg";
GaleryViewModel fotka2 = new GaleryViewModel();
fotka2.LineOne = "line2";
fotka2.ImgURL = "http://www.mykhailenko.com/blog/wp-content/uploads/2010/12/apple-logo-history-2.png";
movie.Images.Add(fotka1);
movie.Images.Add(fotka2);
listImages.ItemsSource = movie.Images;
and it works
I guess the problem is in this line:
if (matches.Count > 0)
You have no matches, so your Url is empty
Can you provide us data your service return to debug code in your context?
Also, why you need loop for this assignment?
foreach (Match match in matches)
fotka.ImgURL = match.Value;