ByeByeByeByeBye Ответов: 5

Проверьте с помощью C#, действительно ли буфер обмена пуст и нет ли там какого-либо формата


Я хотел бы проверить с помощью пользовательского метода C#, похожего на "IsClipBoardEmpty", действительно ли мой буфер обмена пуст. Метод должен быть на чистом C# и не требовать никакого STA_THREAD или внешнего API или чего-то еще. Я пробовал разные решения, которые не работали на меня. У меня есть пункт меню "очистить буфер обмена". Этот пункт меню я хочу отключить, когда буфер обмена пуст. С пустым я имею в виду: он ясен и не имеет ни текста, ни изображений, ни файловых капель, ни графики, ни каких-либо других пользовательских объектов внутри. Я видел разные методы с требованием "STA_THREAD", но мне нужна функция, не требующая STA_THREAD.

Вот мои попытки пользовательского кода, которые не работали:

//1
DataObject retrievedData = (DataObject)Clipboard.GetDataObject();
if(retrievedData==null)
{
toolStripMenuItem1.enabled=false;
}

//2
object retrievedData = ClipBoard.GetText();
if(retrievedData==null)
{
toolStripMenuItem1.enabled=false;
}

//3
if((Clipboard.GetText().toString()!="")
{
toolStripMenuItem1.enabled=true;
}
else
{
toolStripMenuItem1.enabled=false;
}


Что я уже пробовал:

Я выложил функции,которые уже пробовал. Ничего не помогало.

Это тоже не работает. функция всегда возвращает false,
даже если буфер обмена уже пуст.

public static bool IsClipboardEmpty() {
   return (Clipboard.GetDataObject() == null);
}


И то, что мне нужно, - это метод расширения типа "буфер обмена.IsEmpty ()".
Я уже пытался добавить все существующие операторы using и ссылки для C# и увеличил размер моего приложения до 300 МБ. В любом случае метод расширения остается со всеми ссылками на все совместимые DLL-файлы в системе, все еще неизвестные. Есть идеи, какая DLL может содержать такую функцию?

5 Ответов

Рейтинг:
41

Jochen Arndt

Согласно документации по адресу Буфер обмена.Метод GetDataObject (System.Окна)[^] так и должно быть NULL когда нет данных:

Цитата:
Объект данных, который обеспечивает доступ ко всему содержимому системного буфера обмена, или null, если в буфере обмена нет данных.

Если это не так NULL, вы можете проверить доступные форматы с помощью Объект dataObject.Метод GetFormats (Boolean) (System.Окна)[^].

Я не тестировал его, но возвращаемый строковый массив должен быть пустым, когда в буфере обмена нет данных.

Если это тоже не работает (и есть данные для возвращаемых форматов), это может быть невозможно без использования потока STA.


Jochen Arndt

Спасибо за обратную связь и принятие решения.

Хорошо знать, что это работает, потому что об этом иногда спрашивают.

Рейтинг:
35

CHill60

Вместо того чтобы использовать Clipboard.Getxxxx методы используют Clipboard.ContainsData метод.

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

using System.Linq;
...
public static bool IsClipboardEmpty()
{
    var dataFormats = typeof(DataFormats).GetFields(BindingFlags.Public | BindingFlags.Static)
                       .Select(f => f.Name)
                       .ToList();
    var containsSomething = dataFormats.Aggregate(false, (current, x) => current || Clipboard.ContainsData(x));

    return (!containsSomething);
}

Я проверил это с помощью простого таймера
private void timer1_Tick(object sender, EventArgs e)
{
    menuStrip1.Items[0].Enabled = !IsClipboardEmpty();
}
а также путем копирования различных элементов (картинок, текста, файлов) и переключения меню между включенными и отключенными.

[EDIT - для полноты картины здесь то же самое, что и в .NET 2.0]
using System.Collections.Generic;
using System.Reflection;
...
public static bool IsClipboardEmpty()
{
    var dataFormats = new List<string>();
    foreach (var x in typeof (DataFormats).GetFields(BindingFlags.Public | BindingFlags.Static))
        dataFormats.Add(x.Name);

    var containsSomething = false;
    foreach(var y in dataFormats)
        containsSomething = containsSomething || Clipboard.ContainsData(y);

    return (!containsSomething);
}


[EDIT] см. решение 5 ниже от Matt T Heffron для повышения эффективности этого решения, т. е. использования .Any вместо .Aggregate в версии Linq и в версии. NET2 выходите из цикла, как только что-то найдено, например
foreach (var y in dataFormats)
{
    containsSomething = containsSomething || Clipboard.ContainsData(y);
    if (containsSomething) break;
}


CHill60

Просто для полноты картины я добавил в свое решение версию, ориентированную на .Net 2.0, на случай, если кто-то еще столкнется с подобной проблемой.
Всего наилучшего.

Рейтинг:
25

Matt T Heffron

Небольшое улучшение по сравнению с решением 3 на @CHill60
Похоже .Aggregate(...) может быть более эффективным, как .Any(...), любить:

using System.Linq;
...
public static bool IsClipboardEmpty()
{
    var dataFormats = typeof(DataFormats).GetFields(BindingFlags.Public | BindingFlags.Static)
                       .Select(f => f.Name);
    var containsSomething = dataFormats.Any(x => Clipboard.ContainsData(x));
 
    return (!containsSomething);
}

То .Any(...) остановится, как только его найдут. .Aggregate(...) продолжим через всю коллекцию dataFormats.
На .Net версии 2.0 может, кроме того, ранний выход после того, как Clipboard.ContainsData(y) вернуть true.


CHill60

Хорошенький!
Теперь я пойду и опущу голову от стыда. :)

Рейтинг:
2

phil.o

Есть GetDataObject метод, который вы можете использовать; если он возвращает нулевое значение, то буфер обмена пуст:

public static bool IsClipboardEmpty() {
   return (Clipboard.GetDataObject() == null);
}


Рейтинг:
2

Dave Kreskowiak

Единственный раз, когда буфер обмена действительно пуст, - это сразу после входа в систему или чего-то, что вызывает эквивалент буфера обмена.Clear (), что редко когда случается.