2012-04-18 3 views
0

EDIT: добавлен кодItemsControl Bar Chart Scaling баров

Кроме того, так как DateTimes не очень DateTimes (Strings в формате чч: мм: сс) я решил просто использовать строки вместо и использовать TimeSpan для извлечения totalMinutes.

<ObjectDataProvider x:Key="odpLbGrafiek" ObjectType="{x:Type myClasses:GrafiekBar}" MethodName="GetDataGrafiek"/> 

    <DataTemplate x:Key="GrafiekItemTemplate"> 
     <Border Width="Auto" Height="Auto"> 
      <Grid> 
       <Rectangle StrokeThickness="0" Height="30" 
          Margin="15" 
          HorizontalAlignment="Right" 
          VerticalAlignment="Bottom" 
          Width="{Binding Value}" 
          Fill="{Binding Fill}"> 
        <Rectangle.LayoutTransform> 
         <ScaleTransform ScaleX="20" /> 
        </Rectangle.LayoutTransform> 
       </Rectangle> 
      </Grid> 
     </Border> 
    </DataTemplate> 

Заполнение фактически дает размер на панели гистограммы.

ItemsControl:

<ItemsControl x:Name="icGrafiek" 
      Margin="20,3,0,0" 
      ItemsSource="{Binding Source={StaticResource odpLbGrafiek}}" 
      ItemTemplate="{DynamicResource GrafiekItemTemplate}" 
      RenderTransformOrigin="1,0.5" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.RowSpan="6"> 
      <ItemsControl.RenderTransform> 
       <TransformGroup> 
        <ScaleTransform ScaleY="-1" ScaleX="1"/> 
        <SkewTransform AngleY="0" AngleX="0"/> 
        <RotateTransform Angle="180"/> 
        <TranslateTransform/> 
       </TransformGroup> 
      </ItemsControl.RenderTransform> 
     </ItemsControl> 

Следующий метод вызывается в DataBinding. Там bar.Value дает значение для значения Width в datatemplate, которое дает размер бара.

public ObservableCollection<GrafiekBar> GetDataGrafiek() 
    { 
     var converter = new System.Windows.Media.BrushConverter(); 

     Double maxValueStilstanden = GetLargestValueStilstanden(); 

     TimeSpan tsMaxValue = TimeSpan.Parse(maxValueStilstanden.ToString()); 
     totalMinutesMaxValue = tsMaxValue.TotalMinutes; 

     //calculate % of stilstanden Values 
     foreach(String t in stilStandenList) 
     { 
      TimeSpan ts = TimeSpan.Parse(t); 
      Double totalMin = ts.TotalMinutes; 

      totalMin = totalMin/totalMinutesMaxValue * 100; 

      valuesChartPercentage.Add(totalMin); 
     } 

     for (int j = 0; j < valuesChartPercentage.Count; j++) 
     { 
      GrafiekBar bar = new GrafiekBar(); 
      bar.Value = valuesChartPercentage[j]; 
      bar.Fill = converter.ConvertFromString(kleuren[j]) as Brush; 
      listGrafiek.Add(bar); 
     } 

     return listGrafiek; 
    } 

Другой проблемой является фактически ширина (размер бара). Я действительно должен сделать * 10000, чтобы получить визуальное представление о баре.


Я использую ItemsControl что стилизованный так выглядит гистограмму.

Так, например, массив с 5 значениями, идущими от 1 до 5, создает 5 баров с различными размерами бара и с каждым другим цветом. похожи друг на друга в следующем примере http://www.c-sharpcorner.com/uploadfile/mahesh/bar-chart-in-wpf/

Проблема:

Моя проблема с масштабом проклейки стержней (ширины свойства в этом случае, так что INT/двойное значение требуется).

  • Моя программа записывает несколько DateTimes в ЧЧ: мм: сс
  • Стержни должны быть расширены на этих DateTimes

Например я мог иметь бар с 01:22:11 или его бар с 00:01:11 до максимальной суммы 6.

Какой был бы лучший способ масштабирования этих значений DateTime до определенного двойного значения? Это значение будет использоваться для указания размера панели на диаграмме.

Угадайте, что я ищу какой-то расчет, который вычисляет все мои значения одинаково, поэтому я не получаю внезапное значение, которое безумно велико и выходит из моего интерфейса.

Самое чистое решение состоит в том, что все бары сравниваются друг с другом, а когда каждый изменяет другой, он растет/сжимается, но это еще не требуется, хотя это не так сложно, как кажется.

Барный символ сам по себе не должен быть слишком точным, он служит только для того, чтобы получить общую картину ситуации. Точные значения будут записаны в базе данных.

Любые предложения приветствуются!

Спасибо PeterP.

ответ

1

я выбрал бы дату основания и сделать вашу коллекцию графиком количества дней/часов/минут между базовой датой и датой данных

Возможно, вы сможете это сделать с преобразователем, где вы передаете базовую дату в качестве параметра преобразователя.

Вашей два пример дата (01:22:11 и 00:01:11) фактически раз, так что в этом случае я бы просто график количества минут, так как 0, так что фактические значения данных для построения графиков будут 82 и 1

В ответ на ваш вопрос, задающий вопрос о масштабировании, вы будете отображать все в процентах. В этом случае возьмите наибольшее количество и нарисуйте любое другое число, исходя из процента от наибольшего числа.

Итак, используя ваши два примера, я бы преобразовал их в числа 82 и 1, увеличил число (82) на 100% и вернул список, содержащий процент каждого номера до 82, поэтому возврат список будет содержать 100% и 1,2% (1/82).

Вы все еще можете сделать это либо ViewModel или ItemsSource конвертер (преобразователь будет принимать весь список в качестве параметра, и возвращает весь список для возвращаемого значения)


Редактировать

В ответ на ваши комментарии ниже, вот как я установил его, используя Converter на ItemsSource. Преобразователь просто берет значение данных и преобразует его в другое значение, которое специфично только для пользовательского интерфейса дисплея.

Исходный XAML будет выглядеть следующим образом:

<ItemsControl ItemsSource="{Binding MyCollection, 
    Converter="{StaticResource MyTimeConverter}}" /> 

где MyCollection является ObservableCollection<DateTime> и MyTimeConverter делает следующее:

  1. Cast переданному в value как ObservableCollection<DateTime>, так как все параметры преобразователя являются принятой в качестве object
  2. Пройдите через коллекцию и выясните самый большой тим e
  3. Примите самое большое время в сборнике и выясните, сколько минут у него есть. Это будет ваш 100% значения, которое вы будете основывать все другие времена прочь, поэтому хранить количество минут, ваше самое большое время в переменных
  4. Создать новую List<decimal> для возвращаемого значения
  5. Loop через начало сбора. За каждый раз в коллекции разделите его на «100% -ное значение», которое вы сохранили на шаге 3, что даст вам процент от того, как долго этот бар следует сравнивать с наибольшим значением. Добавить этот процент к обращению List<decimal>
  6. Возврат List<decimal> к ItemsControl. Это List<decimal> будет использоваться в качестве ItemsSource вместо фактического ObservableCollection<DateTime>

Это означает, что ваш ItemsControl теперь связан с коллекцией десятичных знаков, где одна величина составляет 100%, и возьмет на себя всю ширину экрана, все другие значения масштабируются до максимального значения.

Что касается вашего вопроса об использовании таймера для обновления коллекции, ваш таймер должен обновить ObservableCollection<DateTime> под названием MyCollection, к которому привязан ItemsControl. Таймер вообще не должен знать и не заботиться о коде конвертера.

Например, если таймер хочет воссоздать MyCollection с совершенно новым набором раз, то он может, а пользовательский интерфейс будет автоматически повторно запустить код преобразователя и обновить гистограмму так ObservableCollections покажет интерфейс когда коллекция изменилась, и пользовательский интерфейс должен обновиться.

Что касается «базовой даты», о которой я говорил в своих комментариях ниже, если вы планировали набор дат вместо времени, вам нужна базовая дата, чтобы ваш график не растягивался до 1/1/0001. Вы не хотите использовать минимальную дату, так как это приведет к тому, что ваше минимальное значение будет отображаться как 0 в вашей гистограмме, поэтому вы должны передать Converter конкретную дату для использования в качестве отправной точки на вашем графике. Если базовая дата была 1/1/12, а ваша самая большая дата была 3/1/12, тогда ваш график растянулся с 1/1/12 до 3/1/12.

Базовая дата будет использоваться на шагах 3 и 5 преобразователя. Например, вместо того, чтобы получать количество минут за это время, вы можете получить количество дней между базовой датой и датой данных.

Вы также можете рассчитать базовую дату в конвертере, например, за 10 дней до самой низкой даты, хотя это может исказить график больше, чем вы предпочитаете, в зависимости от данных.

+0

Спасибо за ответ, можно ли проиллюстрировать часть кода? Моя программа запускает таймер, и его возможные значения обновляются каждые 5 секунд. Возможно также время в секундах. – PeterP

+1

@PeterP Я бы использовал конвертер 'ItemsSource', который ожидает' ObservableCollection ', а также базовый параметр datetime. В конвертере я бы начал, перейдя по списку и найдя самую большую дату. Возьмите разницу в минутах/днях/часах/секундах между базовой датой и самой большой датой и сохраните ее где-нибудь, чтобы использовать ее как 100%. Теперь создайте новый список для хранения возвращаемых значений. Пронумеруйте исходный список, получите количество минут/дней/часов/сек между этой датой и базовой датой и разделите его на 100% число и добавьте результат в список возврата. Возврат нового списка в IC – Rachel

+1

@PeterP Таймер не будет проблемой, если вы используете 'ObservableCollection ' вместо 'List ', так как 'ObservableCollection' будет реагировать на уведомления об изменениях, которые может запускаться таймер. – Rachel

0

Похоже, вам просто нужно отслеживать максимум всех значений. Затем, масштабировать каждое значение в зависимости от этого максимума:

var scale = value/maximum; 
var height = scale * ActualHeight; 

Конечно, так, как вы на самом деле установить высоту каждого элемента, вероятно, будет с помощью привязки.

0

imho у вас вся гистограмма должна иметь ViewModel в задней части, и эта виртуальная машина имеет логику для вычисления максимального значения, а затем определяет масштабирование.

1

Нормализовать.

  • Конвертировать общее время в секундах.
  • (Разделить общую доступную ширину по общему времени) = Коэффициент образования
  • , чтобы найти надлежащую ширину каждой панели, умножать свое время за счет Коэффициент образования

EDIT: выбрать подходящую дату/время, чтобы измерить от нуля, или вы можете использовать только использовать общие секунды свойства вашего DateTime объектов